diff options
Diffstat (limited to 'drivers')
748 files changed, 55866 insertions, 20369 deletions
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 7423052ece5a..d93cc06f4bf8 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -14,12 +14,12 @@ acpi-y := dsfield.o dsmthdat.o dsopcode.o dswexec.o dswscope.o \ acpi-y += evevent.o evregion.o evsci.o evxfevnt.o \ evmisc.o evrgnini.o evxface.o evxfregn.o \ - evgpe.o evgpeblk.o + evgpe.o evgpeblk.o evgpeinit.o evgpeutil.o acpi-y += exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\ exconvrt.o exfldio.o exoparg1.o exprep.o exresop.o exsystem.o\ excreate.o exmisc.o exoparg2.o exregion.o exstore.o exutils.o \ - exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o + exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o exdebug.o acpi-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o hwvalid.o diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 3e6ba99e4053..64d1e5c2d4ae 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -73,8 +73,10 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node, u32 notify_value); /* - * evgpe - GPE handling and dispatch + * evgpe - Low-level GPE support */ +u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list); + acpi_status acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info); @@ -85,19 +87,13 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, u32 gpe_number); +struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number, + struct acpi_gpe_block_info + *gpe_block); + /* - * evgpeblk + * evgpeblk - Upper-level GPE block support */ -u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info); - -acpi_status -acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context); - -acpi_status -acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info *gpe_block, - void *context); - acpi_status acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, struct acpi_generic_address *gpe_block_address, @@ -116,12 +112,37 @@ u32 acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number); -u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list); +/* + * evgpeinit - GPE initialization and update + */ +acpi_status acpi_ev_gpe_initialize(void); + +void acpi_ev_update_gpes(acpi_owner_id table_owner_id); acpi_status -acpi_ev_check_for_wake_only_gpe(struct acpi_gpe_event_info *gpe_event_info); +acpi_ev_match_gpe_method(acpi_handle obj_handle, + u32 level, void *context, void **return_value); -acpi_status acpi_ev_gpe_initialize(void); +acpi_status +acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +/* + * evgpeutil - GPE utilities + */ +acpi_status +acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context); + +u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info); + +struct acpi_gpe_xrupt_info *acpi_ev_get_gpe_xrupt_block(u32 interrupt_number); + +acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt); + +acpi_status +acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context); /* * evregion - Address Space handling diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index f8dd8f250ac4..9070f1fe8f17 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -112,6 +112,19 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_leave_wake_gpes_disabled, TRUE); */ u8 ACPI_INIT_GLOBAL(acpi_gbl_use_default_register_widths, TRUE); +/* + * Optionally enable output from the AML Debug Object. + */ +u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_aml_debug_object, FALSE); + +/* + * Optionally copy the entire DSDT to local memory (instead of simply + * mapping it.) There are some BIOSs that corrupt or replace the original + * DSDT, creating the need for this option. Default is FALSE, do not copy + * the DSDT. + */ +u8 ACPI_INIT_GLOBAL(acpi_gbl_copy_dsdt_locally, FALSE); + /* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */ struct acpi_table_fadt acpi_gbl_FADT; @@ -145,11 +158,10 @@ ACPI_EXTERN u32 acpi_gbl_trace_dbg_layer; ****************************************************************************/ /* - * acpi_gbl_root_table_list is the master list of ACPI tables found in the - * RSDT/XSDT. - * + * acpi_gbl_root_table_list is the master list of ACPI tables that were + * found in the RSDT/XSDT. */ -ACPI_EXTERN struct acpi_internal_rsdt acpi_gbl_root_table_list; +ACPI_EXTERN struct acpi_table_list acpi_gbl_root_table_list; ACPI_EXTERN struct acpi_table_facs *acpi_gbl_FACS; /* These addresses are calculated from the FADT Event Block addresses */ @@ -160,6 +172,11 @@ ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_enable; ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_status; ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_enable; +/* DSDT information. Used to check for DSDT corruption */ + +ACPI_EXTERN struct acpi_table_header *acpi_gbl_DSDT; +ACPI_EXTERN struct acpi_table_header acpi_gbl_original_dsdt_header; + /* * Handle both ACPI 1.0 and ACPI 2.0 Integer widths. The integer width is * determined by the revision of the DSDT: If the DSDT revision is less than diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index 6df3f8428168..049e203bd621 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -121,6 +121,13 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, struct acpi_walk_state *walk_state); /* + * exdebug - AML debug object + */ +void +acpi_ex_do_debug_object(union acpi_operand_object *source_desc, + u32 level, u32 index); + +/* * exfield - ACPI AML (p-code) execution - field manipulation */ acpi_status @@ -274,7 +281,7 @@ acpi_status acpi_ex_system_do_notify_op(union acpi_operand_object *value, union acpi_operand_object *obj_desc); -acpi_status acpi_ex_system_do_suspend(u64 time); +acpi_status acpi_ex_system_do_sleep(u64 time); acpi_status acpi_ex_system_do_stall(u32 time); diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 24b8faa5c395..147a7e6bd38f 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -213,12 +213,12 @@ struct acpi_namespace_node { #define ANOBJ_IS_BIT_OFFSET 0x40 /* i_aSL only: Reference is a bit offset */ #define ANOBJ_IS_REFERENCED 0x80 /* i_aSL only: Object was referenced */ -/* One internal RSDT for table management */ +/* Internal ACPI table management - master table list */ -struct acpi_internal_rsdt { - struct acpi_table_desc *tables; - u32 count; - u32 size; +struct acpi_table_list { + struct acpi_table_desc *tables; /* Table descriptor array */ + u32 current_table_count; /* Tables currently in the array */ + u32 max_table_count; /* Max tables array will hold */ u8 flags; }; @@ -427,8 +427,8 @@ struct acpi_gpe_event_info { struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ u8 flags; /* Misc info about this GPE */ u8 gpe_number; /* This GPE */ - u8 runtime_count; - u8 wakeup_count; + u8 runtime_count; /* References to a run GPE */ + u8 wakeup_count; /* References to a wake GPE */ }; /* Information about a GPE register pair, one per each status/enable pair in an array */ @@ -454,6 +454,7 @@ struct acpi_gpe_block_info { struct acpi_gpe_event_info *event_info; /* One for each GPE */ struct acpi_generic_address block_address; /* Base address of the block */ u32 register_count; /* Number of register pairs in block */ + u16 gpe_count; /* Number of individual GPEs in block */ u8 block_base_number; /* Base GPE number for this block */ }; @@ -469,6 +470,10 @@ struct acpi_gpe_xrupt_info { struct acpi_gpe_walk_info { struct acpi_namespace_node *gpe_device; struct acpi_gpe_block_info *gpe_block; + u16 count; + acpi_owner_id owner_id; + u8 enable_this_gpe; + u8 execute_by_owner_id; }; struct acpi_gpe_device_info { diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 8ff3b741df28..62a576e34361 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -107,6 +107,10 @@ u8 acpi_tb_checksum(u8 *buffer, u32 length); acpi_status acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length); +void acpi_tb_check_dsdt_header(void); + +struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index); + void acpi_tb_install_table(acpi_physical_address address, char *signature, u32 table_index); diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index bb13817e0c31..347bee1726f1 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -323,7 +323,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info, default: ACPI_ERROR((AE_INFO, - "Invalid opcode in field list: %X", + "Invalid opcode in field list: 0x%X", arg->common.aml_opcode)); return_ACPI_STATUS(AE_AML_BAD_OPCODE); } diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 721039233aa7..2a9a561c2f01 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -225,7 +225,7 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, (walk_state->thread->current_sync_level > obj_desc->method.mutex->mutex.sync_level)) { ACPI_ERROR((AE_INFO, - "Cannot acquire Mutex for method [%4.4s], current SyncLevel is too large (%d)", + "Cannot acquire Mutex for method [%4.4s], current SyncLevel is too large (%u)", acpi_ut_get_node_name(method_node), walk_state->thread->current_sync_level)); diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c index cc343b959540..f3d52f59250b 100644 --- a/drivers/acpi/acpica/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -262,7 +262,7 @@ acpi_ds_method_data_get_node(u8 type, if (index > ACPI_METHOD_MAX_LOCAL) { ACPI_ERROR((AE_INFO, - "Local index %d is invalid (max %d)", + "Local index %u is invalid (max %u)", index, ACPI_METHOD_MAX_LOCAL)); return_ACPI_STATUS(AE_AML_INVALID_INDEX); } @@ -276,7 +276,7 @@ acpi_ds_method_data_get_node(u8 type, if (index > ACPI_METHOD_MAX_ARG) { ACPI_ERROR((AE_INFO, - "Arg index %d is invalid (max %d)", + "Arg index %u is invalid (max %u)", index, ACPI_METHOD_MAX_ARG)); return_ACPI_STATUS(AE_AML_INVALID_INDEX); } @@ -287,7 +287,7 @@ acpi_ds_method_data_get_node(u8 type, break; default: - ACPI_ERROR((AE_INFO, "Type %d is invalid", type)); + ACPI_ERROR((AE_INFO, "Type %u is invalid", type)); return_ACPI_STATUS(AE_TYPE); } @@ -424,7 +424,7 @@ acpi_ds_method_data_get_value(u8 type, case ACPI_REFCLASS_ARG: ACPI_ERROR((AE_INFO, - "Uninitialized Arg[%d] at node %p", + "Uninitialized Arg[%u] at node %p", index, node)); return_ACPI_STATUS(AE_AML_UNINITIALIZED_ARG); @@ -440,7 +440,7 @@ acpi_ds_method_data_get_value(u8 type, default: ACPI_ERROR((AE_INFO, - "Not a Arg/Local opcode: %X", + "Not a Arg/Local opcode: 0x%X", type)); return_ACPI_STATUS(AE_AML_INTERNAL); } diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index 891e08bf560b..3607adcaf085 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -288,7 +288,7 @@ acpi_ds_build_internal_buffer_obj(struct acpi_walk_state *walk_state, if (byte_list) { if (byte_list->common.aml_opcode != AML_INT_BYTELIST_OP) { ACPI_ERROR((AE_INFO, - "Expecting bytelist, got AML opcode %X in op %p", + "Expecting bytelist, found AML opcode 0x%X in op %p", byte_list->common.aml_opcode, byte_list)); acpi_ut_remove_reference(obj_desc); @@ -511,7 +511,7 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, } ACPI_INFO((AE_INFO, - "Actual Package length (0x%X) is larger than NumElements field (0x%X), truncated\n", + "Actual Package length (%u) is larger than NumElements field (%u), truncated\n", i, element_count)); } else if (i < element_count) { /* @@ -519,7 +519,7 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, * Note: this is not an error, the package is padded out with NULLs. */ ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Package List length (0x%X) smaller than NumElements count (0x%X), padded with null elements\n", + "Package List length (%u) smaller than NumElements count (%u), padded with null elements\n", i, element_count)); } @@ -701,7 +701,7 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, default: ACPI_ERROR((AE_INFO, - "Unknown constant opcode %X", + "Unknown constant opcode 0x%X", opcode)); status = AE_AML_OPERAND_TYPE; break; @@ -717,7 +717,7 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, break; default: - ACPI_ERROR((AE_INFO, "Unknown Integer type %X", + ACPI_ERROR((AE_INFO, "Unknown Integer type 0x%X", op_info->type)); status = AE_AML_OPERAND_TYPE; break; @@ -806,7 +806,7 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, default: ACPI_ERROR((AE_INFO, - "Unimplemented reference type for AML opcode: %4.4X", + "Unimplemented reference type for AML opcode: 0x%4.4X", opcode)); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } @@ -816,7 +816,7 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, default: - ACPI_ERROR((AE_INFO, "Unimplemented data type: %X", + ACPI_ERROR((AE_INFO, "Unimplemented data type: 0x%X", obj_desc->common.type)); status = AE_AML_OPERAND_TYPE; diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index bf980cadb1e8..53a7e416f33e 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -292,7 +292,7 @@ acpi_status acpi_ds_get_buffer_arguments(union acpi_operand_object *obj_desc) node = obj_desc->buffer.node; if (!node) { ACPI_ERROR((AE_INFO, - "No pointer back to NS node in buffer obj %p", + "No pointer back to namespace node in buffer object %p", obj_desc)); return_ACPI_STATUS(AE_AML_INTERNAL); } @@ -336,7 +336,7 @@ acpi_status acpi_ds_get_package_arguments(union acpi_operand_object *obj_desc) node = obj_desc->package.node; if (!node) { ACPI_ERROR((AE_INFO, - "No pointer back to NS node in package %p", + "No pointer back to namespace node in package %p", obj_desc)); return_ACPI_STATUS(AE_AML_INTERNAL); } @@ -580,7 +580,8 @@ acpi_ds_init_buffer_field(u16 aml_opcode, default: ACPI_ERROR((AE_INFO, - "Unknown field creation opcode %02x", aml_opcode)); + "Unknown field creation opcode 0x%02X", + aml_opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; } @@ -589,7 +590,7 @@ acpi_ds_init_buffer_field(u16 aml_opcode, if ((bit_offset + bit_count) > (8 * (u32) buffer_desc->buffer.length)) { ACPI_ERROR((AE_INFO, - "Field [%4.4s] at %d exceeds Buffer [%4.4s] size %d (bits)", + "Field [%4.4s] at %u exceeds Buffer [%4.4s] size %u (bits)", acpi_ut_get_node_name(result_desc), bit_offset + bit_count, acpi_ut_get_node_name(buffer_desc->buffer.node), @@ -693,7 +694,7 @@ acpi_ds_eval_buffer_field_operands(struct acpi_walk_state *walk_state, status = acpi_ex_resolve_operands(op->common.aml_opcode, ACPI_WALK_OPERANDS, walk_state); if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, "(%s) bad operand(s) (%X)", + ACPI_ERROR((AE_INFO, "(%s) bad operand(s), status 0x%X", acpi_ps_get_opcode_name(op->common.aml_opcode), status)); @@ -1461,7 +1462,7 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state, default: - ACPI_ERROR((AE_INFO, "Unknown control opcode=%X Op=%p", + ACPI_ERROR((AE_INFO, "Unknown control opcode=0x%X Op=%p", op->common.aml_opcode, op)); status = AE_AML_BAD_OPCODE; diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index 6b76c486d784..d555b374e314 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -140,7 +140,7 @@ acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state, if (local_obj_desc->common.type != ACPI_TYPE_INTEGER) { ACPI_ERROR((AE_INFO, - "Bad predicate (not an integer) ObjDesc=%p State=%p Type=%X", + "Bad predicate (not an integer) ObjDesc=%p State=%p Type=0x%X", obj_desc, walk_state, obj_desc->common.type)); status = AE_AML_OPERAND_TYPE; @@ -354,7 +354,7 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state) op_class = walk_state->op_info->class; if (op_class == AML_CLASS_UNKNOWN) { - ACPI_ERROR((AE_INFO, "Unknown opcode %X", + ACPI_ERROR((AE_INFO, "Unknown opcode 0x%X", op->common.aml_opcode)); return_ACPI_STATUS(AE_NOT_IMPLEMENTED); } @@ -678,7 +678,7 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state) default: ACPI_ERROR((AE_INFO, - "Unimplemented opcode, class=%X type=%X Opcode=%X Op=%p", + "Unimplemented opcode, class=0x%X type=0x%X Opcode=-0x%X Op=%p", op_class, op_type, op->common.aml_opcode, op)); diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c index 050df8164165..83155dd8671e 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -179,7 +179,7 @@ acpi_ds_result_push(union acpi_operand_object * object, if (!object) { ACPI_ERROR((AE_INFO, - "Null Object! Obj=%p State=%p Num=%X", + "Null Object! Obj=%p State=%p Num=%u", object, walk_state, walk_state->result_count)); return (AE_BAD_PARAMETER); } @@ -223,7 +223,7 @@ static acpi_status acpi_ds_result_stack_push(struct acpi_walk_state *walk_state) if (((u32) walk_state->result_size + ACPI_RESULTS_FRAME_OBJ_NUM) > ACPI_RESULTS_OBJ_NUM_MAX) { - ACPI_ERROR((AE_INFO, "Result stack overflow: State=%p Num=%X", + ACPI_ERROR((AE_INFO, "Result stack overflow: State=%p Num=%u", walk_state, walk_state->result_size)); return (AE_STACK_OVERFLOW); } @@ -314,7 +314,7 @@ acpi_ds_obj_stack_push(void *object, struct acpi_walk_state * walk_state) if (walk_state->num_operands >= ACPI_OBJ_NUM_OPERANDS) { ACPI_ERROR((AE_INFO, - "Object stack overflow! Obj=%p State=%p #Ops=%X", + "Object stack overflow! Obj=%p State=%p #Ops=%u", object, walk_state, walk_state->num_operands)); return (AE_STACK_OVERFLOW); } @@ -365,7 +365,7 @@ acpi_ds_obj_stack_pop(u32 pop_count, struct acpi_walk_state * walk_state) if (walk_state->num_operands == 0) { ACPI_ERROR((AE_INFO, - "Object stack underflow! Count=%X State=%p #Ops=%X", + "Object stack underflow! Count=%X State=%p #Ops=%u", pop_count, walk_state, walk_state->num_operands)); return (AE_STACK_UNDERFLOW); @@ -377,7 +377,7 @@ acpi_ds_obj_stack_pop(u32 pop_count, struct acpi_walk_state * walk_state) walk_state->operands[walk_state->num_operands] = NULL; } - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Count=%X State=%p #Ops=%X\n", + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Count=%X State=%p #Ops=%u\n", pop_count, walk_state, walk_state->num_operands)); return (AE_OK); diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index c1e6f472d435..f5795915a2e9 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -302,7 +302,7 @@ static u32 acpi_ev_fixed_event_dispatch(u32 event) ACPI_DISABLE_EVENT); ACPI_ERROR((AE_INFO, - "No installed handler for fixed event [%08X]", + "No installed handler for fixed event [0x%08X]", event)); return (ACPI_INTERRUPT_NOT_HANDLED); diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 78c55508aff5..a221ad404167 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -60,7 +60,8 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context); * * RETURN: Status * - * DESCRIPTION: Updates GPE register enable masks based on the GPE type + * DESCRIPTION: Updates GPE register enable masks based upon whether there are + * references (either wake or run) to this GPE * ******************************************************************************/ @@ -81,14 +82,20 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info) (1 << (gpe_event_info->gpe_number - gpe_register_info->base_gpe_number)); + /* Clear the wake/run bits up front */ + ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, register_bit); ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit); - if (gpe_event_info->runtime_count) + /* Set the mask bits only if there are references to this GPE */ + + if (gpe_event_info->runtime_count) { ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit); + } - if (gpe_event_info->wakeup_count) + if (gpe_event_info->wakeup_count) { ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit); + } return_ACPI_STATUS(AE_OK); } @@ -101,7 +108,10 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info) * * RETURN: Status * - * DESCRIPTION: Enable a GPE based on the GPE type + * DESCRIPTION: Hardware-enable a GPE. Always enables the GPE, regardless + * of type or number of references. + * + * Note: The GPE lock should be already acquired when this function is called. * ******************************************************************************/ @@ -109,20 +119,36 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) { acpi_status status; + ACPI_FUNCTION_TRACE(ev_enable_gpe); - /* Make sure HW enable masks are updated */ + + /* + * We will only allow a GPE to be enabled if it has either an + * associated method (_Lxx/_Exx) or a handler. Otherwise, the + * GPE will be immediately disabled by acpi_ev_gpe_dispatch the + * first time it fires. + */ + if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) { + return_ACPI_STATUS(AE_NO_HANDLER); + } + + /* Ensure the HW enable masks are current */ status = acpi_ev_update_gpe_enable_masks(gpe_event_info); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); + } + + /* Clear the GPE (of stale events) */ - /* Clear the GPE (of stale events), then enable it */ status = acpi_hw_clear_gpe(gpe_event_info); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); + } /* Enable the requested GPE */ + status = acpi_hw_write_gpe_enable_reg(gpe_event_info); return_ACPI_STATUS(status); } @@ -135,7 +161,10 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) * * RETURN: Status * - * DESCRIPTION: Disable a GPE based on the GPE type + * DESCRIPTION: Hardware-disable a GPE. Always disables the requested GPE, + * regardless of the type or number of references. + * + * Note: The GPE lock should be already acquired when this function is called. * ******************************************************************************/ @@ -145,24 +174,71 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) ACPI_FUNCTION_TRACE(ev_disable_gpe); - /* Make sure HW enable masks are updated */ + + /* + * Note: Always disable the GPE, even if we think that that it is already + * disabled. It is possible that the AML or some other code has enabled + * the GPE behind our back. + */ + + /* Ensure the HW enable masks are current */ status = acpi_ev_update_gpe_enable_masks(gpe_event_info); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); + } /* - * Even if we don't know the GPE type, make sure that we always - * disable it. low_disable_gpe will just clear the enable bit for this - * GPE and write it. It will not write out the current GPE enable mask, - * since this may inadvertently enable GPEs too early, if a rogue GPE has - * come in during ACPICA initialization - possibly as a result of AML or - * other code that has enabled the GPE. + * Always H/W disable this GPE, even if we don't know the GPE type. + * Simply clear the enable bit for this particular GPE, but do not + * write out the current GPE enable mask since this may inadvertently + * enable GPEs too early. An example is a rogue GPE that has arrived + * during ACPICA initialization - possibly because AML or other code + * has enabled the GPE. */ status = acpi_hw_low_disable_gpe(gpe_event_info); return_ACPI_STATUS(status); } + +/******************************************************************************* + * + * FUNCTION: acpi_ev_low_get_gpe_info + * + * PARAMETERS: gpe_number - Raw GPE number + * gpe_block - A GPE info block + * + * RETURN: A GPE event_info struct. NULL if not a valid GPE (The gpe_number + * is not within the specified GPE block) + * + * DESCRIPTION: Returns the event_info struct associated with this GPE. This is + * the low-level implementation of ev_get_gpe_event_info. + * + ******************************************************************************/ + +struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number, + struct acpi_gpe_block_info + *gpe_block) +{ + u32 gpe_index; + + /* + * Validate that the gpe_number is within the specified gpe_block. + * (Two steps) + */ + if (!gpe_block || (gpe_number < gpe_block->block_base_number)) { + return (NULL); + } + + gpe_index = gpe_number - gpe_block->block_base_number; + if (gpe_index >= gpe_block->gpe_count) { + return (NULL); + } + + return (&gpe_block->event_info[gpe_index]); +} + + /******************************************************************************* * * FUNCTION: acpi_ev_get_gpe_event_info @@ -184,29 +260,23 @@ struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, u32 gpe_number) { union acpi_operand_object *obj_desc; - struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_event_info *gpe_info; u32 i; ACPI_FUNCTION_ENTRY(); - /* A NULL gpe_block means use the FADT-defined GPE block(s) */ + /* A NULL gpe_device means use the FADT-defined GPE block(s) */ if (!gpe_device) { /* Examine GPE Block 0 and 1 (These blocks are permanent) */ for (i = 0; i < ACPI_MAX_GPE_BLOCKS; i++) { - gpe_block = acpi_gbl_gpe_fadt_blocks[i]; - if (gpe_block) { - if ((gpe_number >= gpe_block->block_base_number) - && (gpe_number < - gpe_block->block_base_number + - (gpe_block->register_count * 8))) { - return (&gpe_block-> - event_info[gpe_number - - gpe_block-> - block_base_number]); - } + gpe_info = acpi_ev_low_get_gpe_info(gpe_number, + acpi_gbl_gpe_fadt_blocks + [i]); + if (gpe_info) { + return (gpe_info); } } @@ -223,16 +293,8 @@ struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, return (NULL); } - gpe_block = obj_desc->device.gpe_block; - - if ((gpe_number >= gpe_block->block_base_number) && - (gpe_number < - gpe_block->block_base_number + (gpe_block->register_count * 8))) { - return (&gpe_block-> - event_info[gpe_number - gpe_block->block_base_number]); - } - - return (NULL); + return (acpi_ev_low_get_gpe_info + (gpe_number, obj_desc->device.gpe_block)); } /******************************************************************************* @@ -389,7 +451,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) return_VOID; } - /* Set the GPE flags for return to enabled state */ + /* Update the GPE register masks for return to enabled state */ (void)acpi_ev_update_gpe_enable_masks(gpe_event_info); @@ -499,7 +561,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Unable to clear GPE[%2X]", + "Unable to clear GPE[0x%2X]", gpe_number)); return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); } @@ -532,7 +594,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Unable to clear GPE[%2X]", + "Unable to clear GPE[0x%2X]", gpe_number)); return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); } @@ -548,7 +610,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) status = acpi_ev_disable_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Unable to disable GPE[%2X]", + "Unable to disable GPE[0x%2X]", gpe_number)); return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); } @@ -562,27 +624,30 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Unable to queue handler for GPE[%2X] - event disabled", + "Unable to queue handler for GPE[0x%2X] - event disabled", gpe_number)); } break; default: - /* No handler or method to run! */ - + /* + * No handler or method to run! + * 03/2010: This case should no longer be possible. We will not allow + * a GPE to be enabled if it has no handler or method. + */ ACPI_ERROR((AE_INFO, - "No handler or method for GPE[%2X], disabling event", + "No handler or method for GPE[0x%2X], disabling event", gpe_number)); /* - * Disable the GPE. The GPE will remain disabled until the ACPICA - * Core Subsystem is restarted, or a handler is installed. + * Disable the GPE. The GPE will remain disabled a handler + * is installed or ACPICA is restarted. */ status = acpi_ev_disable_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Unable to disable GPE[%2X]", + "Unable to disable GPE[0x%2X]", gpe_number)); return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); } diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index fef721917eaf..7c28f2d9fd35 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -51,20 +51,6 @@ ACPI_MODULE_NAME("evgpeblk") /* Local prototypes */ static acpi_status -acpi_ev_save_method_info(acpi_handle obj_handle, - u32 level, void *obj_desc, void **return_value); - -static acpi_status -acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, - u32 level, void *info, void **return_value); - -static struct acpi_gpe_xrupt_info *acpi_ev_get_gpe_xrupt_block(u32 - interrupt_number); - -static acpi_status -acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt); - -static acpi_status acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block, u32 interrupt_number); @@ -73,527 +59,6 @@ acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block); /******************************************************************************* * - * FUNCTION: acpi_ev_valid_gpe_event - * - * PARAMETERS: gpe_event_info - Info for this GPE - * - * RETURN: TRUE if the gpe_event is valid - * - * DESCRIPTION: Validate a GPE event. DO NOT CALL FROM INTERRUPT LEVEL. - * Should be called only when the GPE lists are semaphore locked - * and not subject to change. - * - ******************************************************************************/ - -u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info) -{ - struct acpi_gpe_xrupt_info *gpe_xrupt_block; - struct acpi_gpe_block_info *gpe_block; - - ACPI_FUNCTION_ENTRY(); - - /* No need for spin lock since we are not changing any list elements */ - - /* Walk the GPE interrupt levels */ - - gpe_xrupt_block = acpi_gbl_gpe_xrupt_list_head; - while (gpe_xrupt_block) { - gpe_block = gpe_xrupt_block->gpe_block_list_head; - - /* Walk the GPE blocks on this interrupt level */ - - while (gpe_block) { - if ((&gpe_block->event_info[0] <= gpe_event_info) && - (&gpe_block->event_info[((acpi_size) - gpe_block-> - register_count) * 8] > - gpe_event_info)) { - return (TRUE); - } - - gpe_block = gpe_block->next; - } - - gpe_xrupt_block = gpe_xrupt_block->next; - } - - return (FALSE); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ev_walk_gpe_list - * - * PARAMETERS: gpe_walk_callback - Routine called for each GPE block - * Context - Value passed to callback - * - * RETURN: Status - * - * DESCRIPTION: Walk the GPE lists. - * - ******************************************************************************/ - -acpi_status -acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context) -{ - struct acpi_gpe_block_info *gpe_block; - struct acpi_gpe_xrupt_info *gpe_xrupt_info; - acpi_status status = AE_OK; - acpi_cpu_flags flags; - - ACPI_FUNCTION_TRACE(ev_walk_gpe_list); - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - /* Walk the interrupt level descriptor list */ - - gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; - while (gpe_xrupt_info) { - - /* Walk all Gpe Blocks attached to this interrupt level */ - - gpe_block = gpe_xrupt_info->gpe_block_list_head; - while (gpe_block) { - - /* One callback per GPE block */ - - status = - gpe_walk_callback(gpe_xrupt_info, gpe_block, - context); - if (ACPI_FAILURE(status)) { - if (status == AE_CTRL_END) { /* Callback abort */ - status = AE_OK; - } - goto unlock_and_exit; - } - - gpe_block = gpe_block->next; - } - - gpe_xrupt_info = gpe_xrupt_info->next; - } - - unlock_and_exit: - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ev_delete_gpe_handlers - * - * PARAMETERS: gpe_xrupt_info - GPE Interrupt info - * gpe_block - Gpe Block info - * - * RETURN: Status - * - * DESCRIPTION: Delete all Handler objects found in the GPE data structs. - * Used only prior to termination. - * - ******************************************************************************/ - -acpi_status -acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info *gpe_block, - void *context) -{ - struct acpi_gpe_event_info *gpe_event_info; - u32 i; - u32 j; - - ACPI_FUNCTION_TRACE(ev_delete_gpe_handlers); - - /* Examine each GPE Register within the block */ - - for (i = 0; i < gpe_block->register_count; i++) { - - /* Now look at the individual GPEs in this byte register */ - - for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { - gpe_event_info = &gpe_block->event_info[((acpi_size) i * - ACPI_GPE_REGISTER_WIDTH) - + j]; - - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_HANDLER) { - ACPI_FREE(gpe_event_info->dispatch.handler); - gpe_event_info->dispatch.handler = NULL; - gpe_event_info->flags &= - ~ACPI_GPE_DISPATCH_MASK; - } - } - } - - return_ACPI_STATUS(AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ev_save_method_info - * - * PARAMETERS: Callback from walk_namespace - * - * RETURN: Status - * - * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a - * control method under the _GPE portion of the namespace. - * Extract the name and GPE type from the object, saving this - * information for quick lookup during GPE dispatch - * - * The name of each GPE control method is of the form: - * "_Lxx" or "_Exx" - * Where: - * L - means that the GPE is level triggered - * E - means that the GPE is edge triggered - * xx - is the GPE number [in HEX] - * - ******************************************************************************/ - -static acpi_status -acpi_ev_save_method_info(acpi_handle obj_handle, - u32 level, void *obj_desc, void **return_value) -{ - struct acpi_gpe_block_info *gpe_block = (void *)obj_desc; - struct acpi_gpe_event_info *gpe_event_info; - u32 gpe_number; - char name[ACPI_NAME_SIZE + 1]; - u8 type; - - ACPI_FUNCTION_TRACE(ev_save_method_info); - - /* - * _Lxx and _Exx GPE method support - * - * 1) Extract the name from the object and convert to a string - */ - ACPI_MOVE_32_TO_32(name, - &((struct acpi_namespace_node *)obj_handle)->name. - integer); - name[ACPI_NAME_SIZE] = 0; - - /* - * 2) Edge/Level determination is based on the 2nd character - * of the method name - * - * NOTE: Default GPE type is RUNTIME. May be changed later to WAKE - * if a _PRW object is found that points to this GPE. - */ - switch (name[1]) { - case 'L': - type = ACPI_GPE_LEVEL_TRIGGERED; - break; - - case 'E': - type = ACPI_GPE_EDGE_TRIGGERED; - break; - - default: - /* Unknown method type, just ignore it! */ - - ACPI_DEBUG_PRINT((ACPI_DB_LOAD, - "Ignoring unknown GPE method type: %s " - "(name not of form _Lxx or _Exx)", name)); - return_ACPI_STATUS(AE_OK); - } - - /* Convert the last two characters of the name to the GPE Number */ - - gpe_number = ACPI_STRTOUL(&name[2], NULL, 16); - if (gpe_number == ACPI_UINT32_MAX) { - - /* Conversion failed; invalid method, just ignore it */ - - ACPI_DEBUG_PRINT((ACPI_DB_LOAD, - "Could not extract GPE number from name: %s " - "(name is not of form _Lxx or _Exx)", name)); - return_ACPI_STATUS(AE_OK); - } - - /* Ensure that we have a valid GPE number for this GPE block */ - - if ((gpe_number < gpe_block->block_base_number) || - (gpe_number >= (gpe_block->block_base_number + - (gpe_block->register_count * 8)))) { - /* - * Not valid for this GPE block, just ignore it. However, it may be - * valid for a different GPE block, since GPE0 and GPE1 methods both - * appear under \_GPE. - */ - return_ACPI_STATUS(AE_OK); - } - - /* - * Now we can add this information to the gpe_event_info block for use - * during dispatch of this GPE. - */ - gpe_event_info = - &gpe_block->event_info[gpe_number - gpe_block->block_base_number]; - - gpe_event_info->flags = (u8) (type | ACPI_GPE_DISPATCH_METHOD); - - gpe_event_info->dispatch.method_node = - (struct acpi_namespace_node *)obj_handle; - - ACPI_DEBUG_PRINT((ACPI_DB_LOAD, - "Registered GPE method %s as GPE number 0x%.2X\n", - name, gpe_number)); - return_ACPI_STATUS(AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ev_match_prw_and_gpe - * - * PARAMETERS: Callback from walk_namespace - * - * RETURN: Status. NOTE: We ignore errors so that the _PRW walk is - * not aborted on a single _PRW failure. - * - * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a - * Device. Run the _PRW method. If present, extract the GPE - * number and mark the GPE as a WAKE GPE. - * - ******************************************************************************/ - -static acpi_status -acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, - u32 level, void *info, void **return_value) -{ - struct acpi_gpe_walk_info *gpe_info = (void *)info; - struct acpi_namespace_node *gpe_device; - struct acpi_gpe_block_info *gpe_block; - struct acpi_namespace_node *target_gpe_device; - struct acpi_gpe_event_info *gpe_event_info; - union acpi_operand_object *pkg_desc; - union acpi_operand_object *obj_desc; - u32 gpe_number; - acpi_status status; - - ACPI_FUNCTION_TRACE(ev_match_prw_and_gpe); - - /* Check for a _PRW method under this device */ - - status = acpi_ut_evaluate_object(obj_handle, METHOD_NAME__PRW, - ACPI_BTYPE_PACKAGE, &pkg_desc); - if (ACPI_FAILURE(status)) { - - /* Ignore all errors from _PRW, we don't want to abort the subsystem */ - - return_ACPI_STATUS(AE_OK); - } - - /* The returned _PRW package must have at least two elements */ - - if (pkg_desc->package.count < 2) { - goto cleanup; - } - - /* Extract pointers from the input context */ - - gpe_device = gpe_info->gpe_device; - gpe_block = gpe_info->gpe_block; - - /* - * The _PRW object must return a package, we are only interested in the - * first element - */ - obj_desc = pkg_desc->package.elements[0]; - - if (obj_desc->common.type == ACPI_TYPE_INTEGER) { - - /* Use FADT-defined GPE device (from definition of _PRW) */ - - target_gpe_device = acpi_gbl_fadt_gpe_device; - - /* Integer is the GPE number in the FADT described GPE blocks */ - - gpe_number = (u32) obj_desc->integer.value; - } else if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { - - /* Package contains a GPE reference and GPE number within a GPE block */ - - if ((obj_desc->package.count < 2) || - ((obj_desc->package.elements[0])->common.type != - ACPI_TYPE_LOCAL_REFERENCE) || - ((obj_desc->package.elements[1])->common.type != - ACPI_TYPE_INTEGER)) { - goto cleanup; - } - - /* Get GPE block reference and decode */ - - target_gpe_device = - obj_desc->package.elements[0]->reference.node; - gpe_number = (u32) obj_desc->package.elements[1]->integer.value; - } else { - /* Unknown type, just ignore it */ - - goto cleanup; - } - - /* - * Is this GPE within this block? - * - * TRUE if and only if these conditions are true: - * 1) The GPE devices match. - * 2) The GPE index(number) is within the range of the Gpe Block - * associated with the GPE device. - */ - if ((gpe_device == target_gpe_device) && - (gpe_number >= gpe_block->block_base_number) && - (gpe_number < gpe_block->block_base_number + - (gpe_block->register_count * 8))) { - gpe_event_info = &gpe_block->event_info[gpe_number - - gpe_block-> - block_base_number]; - - gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; - } - - cleanup: - acpi_ut_remove_reference(pkg_desc); - return_ACPI_STATUS(AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ev_get_gpe_xrupt_block - * - * PARAMETERS: interrupt_number - Interrupt for a GPE block - * - * RETURN: A GPE interrupt block - * - * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt - * block per unique interrupt level used for GPEs. Should be - * called only when the GPE lists are semaphore locked and not - * subject to change. - * - ******************************************************************************/ - -static struct acpi_gpe_xrupt_info *acpi_ev_get_gpe_xrupt_block(u32 - interrupt_number) -{ - struct acpi_gpe_xrupt_info *next_gpe_xrupt; - struct acpi_gpe_xrupt_info *gpe_xrupt; - acpi_status status; - acpi_cpu_flags flags; - - ACPI_FUNCTION_TRACE(ev_get_gpe_xrupt_block); - - /* No need for lock since we are not changing any list elements here */ - - next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; - while (next_gpe_xrupt) { - if (next_gpe_xrupt->interrupt_number == interrupt_number) { - return_PTR(next_gpe_xrupt); - } - - next_gpe_xrupt = next_gpe_xrupt->next; - } - - /* Not found, must allocate a new xrupt descriptor */ - - gpe_xrupt = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_xrupt_info)); - if (!gpe_xrupt) { - return_PTR(NULL); - } - - gpe_xrupt->interrupt_number = interrupt_number; - - /* Install new interrupt descriptor with spin lock */ - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - if (acpi_gbl_gpe_xrupt_list_head) { - next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; - while (next_gpe_xrupt->next) { - next_gpe_xrupt = next_gpe_xrupt->next; - } - - next_gpe_xrupt->next = gpe_xrupt; - gpe_xrupt->previous = next_gpe_xrupt; - } else { - acpi_gbl_gpe_xrupt_list_head = gpe_xrupt; - } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - - /* Install new interrupt handler if not SCI_INT */ - - if (interrupt_number != acpi_gbl_FADT.sci_interrupt) { - status = acpi_os_install_interrupt_handler(interrupt_number, - acpi_ev_gpe_xrupt_handler, - gpe_xrupt); - if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, - "Could not install GPE interrupt handler at level 0x%X", - interrupt_number)); - return_PTR(NULL); - } - } - - return_PTR(gpe_xrupt); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ev_delete_gpe_xrupt - * - * PARAMETERS: gpe_xrupt - A GPE interrupt info block - * - * RETURN: Status - * - * DESCRIPTION: Remove and free a gpe_xrupt block. Remove an associated - * interrupt handler if not the SCI interrupt. - * - ******************************************************************************/ - -static acpi_status -acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt) -{ - acpi_status status; - acpi_cpu_flags flags; - - ACPI_FUNCTION_TRACE(ev_delete_gpe_xrupt); - - /* We never want to remove the SCI interrupt handler */ - - if (gpe_xrupt->interrupt_number == acpi_gbl_FADT.sci_interrupt) { - gpe_xrupt->gpe_block_list_head = NULL; - return_ACPI_STATUS(AE_OK); - } - - /* Disable this interrupt */ - - status = - acpi_os_remove_interrupt_handler(gpe_xrupt->interrupt_number, - acpi_ev_gpe_xrupt_handler); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - /* Unlink the interrupt block with lock */ - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - if (gpe_xrupt->previous) { - gpe_xrupt->previous->next = gpe_xrupt->next; - } else { - /* No previous, update list head */ - - acpi_gbl_gpe_xrupt_list_head = gpe_xrupt->next; - } - - if (gpe_xrupt->next) { - gpe_xrupt->next->previous = gpe_xrupt->previous; - } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - - /* Free the block */ - - ACPI_FREE(gpe_xrupt); - return_ACPI_STATUS(AE_OK); -} - -/******************************************************************************* - * * FUNCTION: acpi_ev_install_gpe_block * * PARAMETERS: gpe_block - New GPE block @@ -705,8 +170,7 @@ acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block) acpi_os_release_lock(acpi_gbl_gpe_lock, flags); } - acpi_current_gpe_count -= - gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH; + acpi_current_gpe_count -= gpe_block->gpe_count; /* Free the gpe_block */ @@ -760,9 +224,7 @@ acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block) * Allocate the GPE event_info block. There are eight distinct GPEs * per register. Initialization to zeros is sufficient. */ - gpe_event_info = ACPI_ALLOCATE_ZEROED(((acpi_size) gpe_block-> - register_count * - ACPI_GPE_REGISTER_WIDTH) * + gpe_event_info = ACPI_ALLOCATE_ZEROED((acpi_size) gpe_block->gpe_count * sizeof(struct acpi_gpe_event_info)); if (!gpe_event_info) { @@ -880,6 +342,7 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, { acpi_status status; struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_walk_info walk_info; ACPI_FUNCTION_TRACE(ev_create_gpe_block); @@ -897,6 +360,7 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, /* Initialize the new GPE block */ gpe_block->node = gpe_device; + gpe_block->gpe_count = (u16)(register_count * ACPI_GPE_REGISTER_WIDTH); gpe_block->register_count = register_count; gpe_block->block_base_number = gpe_block_base_number; @@ -921,12 +385,17 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, return_ACPI_STATUS(status); } - /* Find all GPE methods (_Lxx, _Exx) for this block */ + /* Find all GPE methods (_Lxx or_Exx) for this block */ + + walk_info.gpe_block = gpe_block; + walk_info.gpe_device = gpe_device; + walk_info.enable_this_gpe = FALSE; + walk_info.execute_by_owner_id = FALSE; status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device, ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK, - acpi_ev_save_method_info, NULL, - gpe_block, NULL); + acpi_ev_match_gpe_method, NULL, + &walk_info, NULL); /* Return the new block */ @@ -938,14 +407,13 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, "GPE %02X to %02X [%4.4s] %u regs on int 0x%X\n", (u32) gpe_block->block_base_number, (u32) (gpe_block->block_base_number + - ((gpe_block->register_count * - ACPI_GPE_REGISTER_WIDTH) - 1)), + (gpe_block->gpe_count - 1)), gpe_device->name.ascii, gpe_block->register_count, interrupt_number)); /* Update global count of currently available GPEs */ - acpi_current_gpe_count += register_count * ACPI_GPE_REGISTER_WIDTH; + acpi_current_gpe_count += gpe_block->gpe_count; return_ACPI_STATUS(AE_OK); } @@ -969,10 +437,13 @@ acpi_status acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, struct acpi_gpe_block_info *gpe_block) { + acpi_status status; struct acpi_gpe_event_info *gpe_event_info; - struct acpi_gpe_walk_info gpe_info; + struct acpi_gpe_walk_info walk_info; u32 wake_gpe_count; u32 gpe_enabled_count; + u32 gpe_index; + u32 gpe_number; u32 i; u32 j; @@ -995,210 +466,75 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, * definition a wake GPE and will not be enabled while the machine * is running. */ - gpe_info.gpe_block = gpe_block; - gpe_info.gpe_device = gpe_device; + walk_info.gpe_block = gpe_block; + walk_info.gpe_device = gpe_device; + walk_info.execute_by_owner_id = FALSE; - acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + status = + acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, acpi_ev_match_prw_and_gpe, NULL, - &gpe_info, NULL); + &walk_info, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While executing _PRW methods")); + } } /* - * Enable all GPEs that have a corresponding method and aren't + * Enable all GPEs that have a corresponding method and are not * capable of generating wakeups. Any other GPEs within this block - * must be enabled via the acpi_enable_gpe() interface. + * must be enabled via the acpi_enable_gpe interface. */ wake_gpe_count = 0; gpe_enabled_count = 0; - if (gpe_device == acpi_gbl_fadt_gpe_device) + + if (gpe_device == acpi_gbl_fadt_gpe_device) { gpe_device = NULL; + } for (i = 0; i < gpe_block->register_count; i++) { for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { - acpi_status status; - acpi_size gpe_index; - int gpe_number; /* Get the info block for this particular GPE */ - gpe_index = (acpi_size)i * ACPI_GPE_REGISTER_WIDTH + j; + + gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j; gpe_event_info = &gpe_block->event_info[gpe_index]; if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) { wake_gpe_count++; - if (acpi_gbl_leave_wake_gpes_disabled) + if (acpi_gbl_leave_wake_gpes_disabled) { continue; + } } - if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)) + /* Ignore GPEs that have no corresponding _Lxx/_Exx method */ + + if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)) { continue; + } + + /* Enable this GPE */ gpe_number = gpe_index + gpe_block->block_base_number; status = acpi_enable_gpe(gpe_device, gpe_number, - ACPI_GPE_TYPE_RUNTIME); - if (ACPI_FAILURE(status)) - ACPI_ERROR((AE_INFO, - "Failed to enable GPE %02X\n", - gpe_number)); - else - gpe_enabled_count++; - } - } - - ACPI_DEBUG_PRINT((ACPI_DB_INIT, - "Found %u Wake, Enabled %u Runtime GPEs in this block\n", - wake_gpe_count, gpe_enabled_count)); - - return_ACPI_STATUS(AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ev_gpe_initialize - * - * PARAMETERS: None - * - * RETURN: Status - * - * DESCRIPTION: Initialize the GPE data structures - * - ******************************************************************************/ - -acpi_status acpi_ev_gpe_initialize(void) -{ - u32 register_count0 = 0; - u32 register_count1 = 0; - u32 gpe_number_max = 0; - acpi_status status; - - ACPI_FUNCTION_TRACE(ev_gpe_initialize); - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - /* - * Initialize the GPE Block(s) defined in the FADT - * - * Why the GPE register block lengths are divided by 2: From the ACPI - * Spec, section "General-Purpose Event Registers", we have: - * - * "Each register block contains two registers of equal length - * GPEx_STS and GPEx_EN (where x is 0 or 1). The length of the - * GPE0_STS and GPE0_EN registers is equal to half the GPE0_LEN - * The length of the GPE1_STS and GPE1_EN registers is equal to - * half the GPE1_LEN. If a generic register block is not supported - * then its respective block pointer and block length values in the - * FADT table contain zeros. The GPE0_LEN and GPE1_LEN do not need - * to be the same size." - */ - - /* - * Determine the maximum GPE number for this machine. - * - * Note: both GPE0 and GPE1 are optional, and either can exist without - * the other. - * - * If EITHER the register length OR the block address are zero, then that - * particular block is not supported. - */ - if (acpi_gbl_FADT.gpe0_block_length && - acpi_gbl_FADT.xgpe0_block.address) { - - /* GPE block 0 exists (has both length and address > 0) */ - - register_count0 = (u16) (acpi_gbl_FADT.gpe0_block_length / 2); - - gpe_number_max = - (register_count0 * ACPI_GPE_REGISTER_WIDTH) - 1; - - /* Install GPE Block 0 */ - - status = acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device, - &acpi_gbl_FADT.xgpe0_block, - register_count0, 0, - acpi_gbl_FADT.sci_interrupt, - &acpi_gbl_gpe_fadt_blocks[0]); - - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Could not create GPE Block 0")); - } - } - - if (acpi_gbl_FADT.gpe1_block_length && - acpi_gbl_FADT.xgpe1_block.address) { - - /* GPE block 1 exists (has both length and address > 0) */ - - register_count1 = (u16) (acpi_gbl_FADT.gpe1_block_length / 2); - - /* Check for GPE0/GPE1 overlap (if both banks exist) */ - - if ((register_count0) && - (gpe_number_max >= acpi_gbl_FADT.gpe1_base)) { - ACPI_ERROR((AE_INFO, - "GPE0 block (GPE 0 to %d) overlaps the GPE1 block " - "(GPE %d to %d) - Ignoring GPE1", - gpe_number_max, acpi_gbl_FADT.gpe1_base, - acpi_gbl_FADT.gpe1_base + - ((register_count1 * - ACPI_GPE_REGISTER_WIDTH) - 1))); - - /* Ignore GPE1 block by setting the register count to zero */ - - register_count1 = 0; - } else { - /* Install GPE Block 1 */ - - status = - acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device, - &acpi_gbl_FADT.xgpe1_block, - register_count1, - acpi_gbl_FADT.gpe1_base, - acpi_gbl_FADT. - sci_interrupt, - &acpi_gbl_gpe_fadt_blocks - [1]); - + ACPI_GPE_TYPE_RUNTIME); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Could not create GPE Block 1")); + "Could not enable GPE 0x%02X", + gpe_number)); + continue; } - /* - * GPE0 and GPE1 do not have to be contiguous in the GPE number - * space. However, GPE0 always starts at GPE number zero. - */ - gpe_number_max = acpi_gbl_FADT.gpe1_base + - ((register_count1 * ACPI_GPE_REGISTER_WIDTH) - 1); + gpe_enabled_count++; } } - /* Exit if there are no GPE registers */ - - if ((register_count0 + register_count1) == 0) { - - /* GPEs are not required by ACPI, this is OK */ - + if (gpe_enabled_count || wake_gpe_count) { ACPI_DEBUG_PRINT((ACPI_DB_INIT, - "There are no GPE blocks defined in the FADT\n")); - status = AE_OK; - goto cleanup; - } - - /* Check for Max GPE number out-of-range */ - - if (gpe_number_max > ACPI_GPE_MAX) { - ACPI_ERROR((AE_INFO, - "Maximum GPE number from FADT is too large: 0x%X", - gpe_number_max)); - status = AE_BAD_VALUE; - goto cleanup; + "Enabled %u Runtime GPEs, added %u Wake GPEs in this block\n", + gpe_enabled_count, wake_gpe_count)); } - cleanup: - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c new file mode 100644 index 000000000000..3f6c2d26410d --- /dev/null +++ b/drivers/acpi/acpica/evgpeinit.c @@ -0,0 +1,653 @@ +/****************************************************************************** + * + * Module Name: evgpeinit - System GPE initialization and update + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2010, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evgpeinit") + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize the GPE data structures and the FADT GPE 0/1 blocks + * + ******************************************************************************/ +acpi_status acpi_ev_gpe_initialize(void) +{ + u32 register_count0 = 0; + u32 register_count1 = 0; + u32 gpe_number_max = 0; + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_gpe_initialize); + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Initialize the GPE Block(s) defined in the FADT + * + * Why the GPE register block lengths are divided by 2: From the ACPI + * Spec, section "General-Purpose Event Registers", we have: + * + * "Each register block contains two registers of equal length + * GPEx_STS and GPEx_EN (where x is 0 or 1). The length of the + * GPE0_STS and GPE0_EN registers is equal to half the GPE0_LEN + * The length of the GPE1_STS and GPE1_EN registers is equal to + * half the GPE1_LEN. If a generic register block is not supported + * then its respective block pointer and block length values in the + * FADT table contain zeros. The GPE0_LEN and GPE1_LEN do not need + * to be the same size." + */ + + /* + * Determine the maximum GPE number for this machine. + * + * Note: both GPE0 and GPE1 are optional, and either can exist without + * the other. + * + * If EITHER the register length OR the block address are zero, then that + * particular block is not supported. + */ + if (acpi_gbl_FADT.gpe0_block_length && + acpi_gbl_FADT.xgpe0_block.address) { + + /* GPE block 0 exists (has both length and address > 0) */ + + register_count0 = (u16)(acpi_gbl_FADT.gpe0_block_length / 2); + + gpe_number_max = + (register_count0 * ACPI_GPE_REGISTER_WIDTH) - 1; + + /* Install GPE Block 0 */ + + status = acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device, + &acpi_gbl_FADT.xgpe0_block, + register_count0, 0, + acpi_gbl_FADT.sci_interrupt, + &acpi_gbl_gpe_fadt_blocks[0]); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not create GPE Block 0")); + } + } + + if (acpi_gbl_FADT.gpe1_block_length && + acpi_gbl_FADT.xgpe1_block.address) { + + /* GPE block 1 exists (has both length and address > 0) */ + + register_count1 = (u16)(acpi_gbl_FADT.gpe1_block_length / 2); + + /* Check for GPE0/GPE1 overlap (if both banks exist) */ + + if ((register_count0) && + (gpe_number_max >= acpi_gbl_FADT.gpe1_base)) { + ACPI_ERROR((AE_INFO, + "GPE0 block (GPE 0 to %u) overlaps the GPE1 block " + "(GPE %u to %u) - Ignoring GPE1", + gpe_number_max, acpi_gbl_FADT.gpe1_base, + acpi_gbl_FADT.gpe1_base + + ((register_count1 * + ACPI_GPE_REGISTER_WIDTH) - 1))); + + /* Ignore GPE1 block by setting the register count to zero */ + + register_count1 = 0; + } else { + /* Install GPE Block 1 */ + + status = + acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device, + &acpi_gbl_FADT.xgpe1_block, + register_count1, + acpi_gbl_FADT.gpe1_base, + acpi_gbl_FADT. + sci_interrupt, + &acpi_gbl_gpe_fadt_blocks + [1]); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not create GPE Block 1")); + } + + /* + * GPE0 and GPE1 do not have to be contiguous in the GPE number + * space. However, GPE0 always starts at GPE number zero. + */ + gpe_number_max = acpi_gbl_FADT.gpe1_base + + ((register_count1 * ACPI_GPE_REGISTER_WIDTH) - 1); + } + } + + /* Exit if there are no GPE registers */ + + if ((register_count0 + register_count1) == 0) { + + /* GPEs are not required by ACPI, this is OK */ + + ACPI_DEBUG_PRINT((ACPI_DB_INIT, + "There are no GPE blocks defined in the FADT\n")); + status = AE_OK; + goto cleanup; + } + + /* Check for Max GPE number out-of-range */ + + if (gpe_number_max > ACPI_GPE_MAX) { + ACPI_ERROR((AE_INFO, + "Maximum GPE number from FADT is too large: 0x%X", + gpe_number_max)); + status = AE_BAD_VALUE; + goto cleanup; + } + + cleanup: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_update_gpes + * + * PARAMETERS: table_owner_id - ID of the newly-loaded ACPI table + * + * RETURN: None + * + * DESCRIPTION: Check for new GPE methods (_Lxx/_Exx) made available as a + * result of a Load() or load_table() operation. If new GPE + * methods have been installed, register the new methods and + * enable and runtime GPEs that are associated with them. Also, + * run any newly loaded _PRW methods in order to discover any + * new CAN_WAKE GPEs. + * + ******************************************************************************/ + +void acpi_ev_update_gpes(acpi_owner_id table_owner_id) +{ + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_walk_info walk_info; + acpi_status status = AE_OK; + u32 new_wake_gpe_count = 0; + + /* We will examine only _PRW/_Lxx/_Exx methods owned by this table */ + + walk_info.owner_id = table_owner_id; + walk_info.execute_by_owner_id = TRUE; + walk_info.count = 0; + + if (acpi_gbl_leave_wake_gpes_disabled) { + /* + * 1) Run any newly-loaded _PRW methods to find any GPEs that + * can now be marked as CAN_WAKE GPEs. Note: We must run the + * _PRW methods before we process the _Lxx/_Exx methods because + * we will enable all runtime GPEs associated with the new + * _Lxx/_Exx methods at the time we process those methods. + * + * Unlock interpreter so that we can run the _PRW methods. + */ + walk_info.gpe_block = NULL; + walk_info.gpe_device = NULL; + + acpi_ex_exit_interpreter(); + + status = + acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + ACPI_NS_WALK_NO_UNLOCK, + acpi_ev_match_prw_and_gpe, NULL, + &walk_info, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While executing _PRW methods")); + } + + acpi_ex_enter_interpreter(); + new_wake_gpe_count = walk_info.count; + } + + /* + * 2) Find any _Lxx/_Exx GPE methods that have just been loaded. + * + * Any GPEs that correspond to new _Lxx/_Exx methods and are not + * marked as CAN_WAKE are immediately enabled. + * + * Examine the namespace underneath each gpe_device within the + * gpe_block lists. + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return; + } + + walk_info.count = 0; + walk_info.enable_this_gpe = TRUE; + + /* Walk the interrupt level descriptor list */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + + /* Walk all Gpe Blocks attached to this interrupt level */ + + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + walk_info.gpe_block = gpe_block; + walk_info.gpe_device = gpe_block->node; + + status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, + walk_info.gpe_device, + ACPI_UINT32_MAX, + ACPI_NS_WALK_NO_UNLOCK, + acpi_ev_match_gpe_method, + NULL, &walk_info, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While decoding _Lxx/_Exx methods")); + } + + gpe_block = gpe_block->next; + } + + gpe_xrupt_info = gpe_xrupt_info->next; + } + + if (walk_info.count || new_wake_gpe_count) { + ACPI_INFO((AE_INFO, + "Enabled %u new runtime GPEs, added %u new wakeup GPEs", + walk_info.count, new_wake_gpe_count)); + } + + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_match_gpe_method + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a + * control method under the _GPE portion of the namespace. + * Extract the name and GPE type from the object, saving this + * information for quick lookup during GPE dispatch. Allows a + * per-owner_id evaluation if execute_by_owner_id is TRUE in the + * walk_info parameter block. + * + * The name of each GPE control method is of the form: + * "_Lxx" or "_Exx", where: + * L - means that the GPE is level triggered + * E - means that the GPE is edge triggered + * xx - is the GPE number [in HEX] + * + * If walk_info->execute_by_owner_id is TRUE, we only execute examine GPE methods + * with that owner. + * If walk_info->enable_this_gpe is TRUE, the GPE that is referred to by a GPE + * method is immediately enabled (Used for Load/load_table operators) + * + ******************************************************************************/ + +acpi_status +acpi_ev_match_gpe_method(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + struct acpi_namespace_node *method_node = + ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + struct acpi_gpe_walk_info *walk_info = + ACPI_CAST_PTR(struct acpi_gpe_walk_info, context); + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_namespace_node *gpe_device; + acpi_status status; + u32 gpe_number; + char name[ACPI_NAME_SIZE + 1]; + u8 type; + + ACPI_FUNCTION_TRACE(ev_match_gpe_method); + + /* Check if requested owner_id matches this owner_id */ + + if ((walk_info->execute_by_owner_id) && + (method_node->owner_id != walk_info->owner_id)) { + return_ACPI_STATUS(AE_OK); + } + + /* + * Match and decode the _Lxx and _Exx GPE method names + * + * 1) Extract the method name and null terminate it + */ + ACPI_MOVE_32_TO_32(name, &method_node->name.integer); + name[ACPI_NAME_SIZE] = 0; + + /* 2) Name must begin with an underscore */ + + if (name[0] != '_') { + return_ACPI_STATUS(AE_OK); /* Ignore this method */ + } + + /* + * 3) Edge/Level determination is based on the 2nd character + * of the method name + * + * NOTE: Default GPE type is RUNTIME only. Later, if a _PRW object is + * found that points to this GPE, the ACPI_GPE_CAN_WAKE flag is set. + */ + switch (name[1]) { + case 'L': + type = ACPI_GPE_LEVEL_TRIGGERED; + break; + + case 'E': + type = ACPI_GPE_EDGE_TRIGGERED; + break; + + default: + /* Unknown method type, just ignore it */ + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "Ignoring unknown GPE method type: %s " + "(name not of form _Lxx or _Exx)", name)); + return_ACPI_STATUS(AE_OK); + } + + /* 4) The last two characters of the name are the hex GPE Number */ + + gpe_number = ACPI_STRTOUL(&name[2], NULL, 16); + if (gpe_number == ACPI_UINT32_MAX) { + + /* Conversion failed; invalid method, just ignore it */ + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "Could not extract GPE number from name: %s " + "(name is not of form _Lxx or _Exx)", name)); + return_ACPI_STATUS(AE_OK); + } + + /* Ensure that we have a valid GPE number for this GPE block */ + + gpe_event_info = + acpi_ev_low_get_gpe_info(gpe_number, walk_info->gpe_block); + if (!gpe_event_info) { + /* + * This gpe_number is not valid for this GPE block, just ignore it. + * However, it may be valid for a different GPE block, since GPE0 + * and GPE1 methods both appear under \_GPE. + */ + return_ACPI_STATUS(AE_OK); + } + + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + ACPI_GPE_DISPATCH_HANDLER) { + + /* If there is already a handler, ignore this GPE method */ + + return_ACPI_STATUS(AE_OK); + } + + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + ACPI_GPE_DISPATCH_METHOD) { + /* + * If there is already a method, ignore this method. But check + * for a type mismatch (if both the _Lxx AND _Exx exist) + */ + if (type != (gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK)) { + ACPI_ERROR((AE_INFO, + "For GPE 0x%.2X, found both _L%2.2X and _E%2.2X methods", + gpe_number, gpe_number, gpe_number)); + } + return_ACPI_STATUS(AE_OK); + } + + /* + * Add the GPE information from above to the gpe_event_info block for + * use during dispatch of this GPE. + */ + gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_METHOD); + gpe_event_info->dispatch.method_node = method_node; + + /* + * Enable this GPE if requested. This only happens when during the + * execution of a Load or load_table operator. We have found a new + * GPE method and want to immediately enable the GPE if it is a + * runtime GPE. + */ + if (walk_info->enable_this_gpe) { + + /* Ignore GPEs that can wake the system */ + + if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE) || + !acpi_gbl_leave_wake_gpes_disabled) { + walk_info->count++; + gpe_device = walk_info->gpe_device; + + if (gpe_device == acpi_gbl_fadt_gpe_device) { + gpe_device = NULL; + } + + status = acpi_enable_gpe(gpe_device, gpe_number, + ACPI_GPE_TYPE_RUNTIME); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not enable GPE 0x%02X", + gpe_number)); + } + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "Registered GPE method %s as GPE number 0x%.2X\n", + name, gpe_number)); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_match_prw_and_gpe + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status. NOTE: We ignore errors so that the _PRW walk is + * not aborted on a single _PRW failure. + * + * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a + * Device. Run the _PRW method. If present, extract the GPE + * number and mark the GPE as a CAN_WAKE GPE. Allows a + * per-owner_id execution if execute_by_owner_id is TRUE in the + * walk_info parameter block. + * + * If walk_info->execute_by_owner_id is TRUE, we only execute _PRWs with that + * owner. + * If walk_info->gpe_device is NULL, we execute every _PRW found. Otherwise, + * we only execute _PRWs that refer to the input gpe_device. + * + ******************************************************************************/ + +acpi_status +acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + struct acpi_gpe_walk_info *walk_info = + ACPI_CAST_PTR(struct acpi_gpe_walk_info, context); + struct acpi_namespace_node *gpe_device; + struct acpi_gpe_block_info *gpe_block; + struct acpi_namespace_node *target_gpe_device; + struct acpi_namespace_node *prw_node; + struct acpi_gpe_event_info *gpe_event_info; + union acpi_operand_object *pkg_desc; + union acpi_operand_object *obj_desc; + u32 gpe_number; + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_match_prw_and_gpe); + + /* Check for a _PRW method under this device */ + + status = acpi_ns_get_node(obj_handle, METHOD_NAME__PRW, + ACPI_NS_NO_UPSEARCH, &prw_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(AE_OK); + } + + /* Check if requested owner_id matches this owner_id */ + + if ((walk_info->execute_by_owner_id) && + (prw_node->owner_id != walk_info->owner_id)) { + return_ACPI_STATUS(AE_OK); + } + + /* Execute the _PRW */ + + status = acpi_ut_evaluate_object(prw_node, NULL, + ACPI_BTYPE_PACKAGE, &pkg_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(AE_OK); + } + + /* The returned _PRW package must have at least two elements */ + + if (pkg_desc->package.count < 2) { + goto cleanup; + } + + /* Extract pointers from the input context */ + + gpe_device = walk_info->gpe_device; + gpe_block = walk_info->gpe_block; + + /* + * The _PRW object must return a package, we are only interested + * in the first element + */ + obj_desc = pkg_desc->package.elements[0]; + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + + /* Use FADT-defined GPE device (from definition of _PRW) */ + + target_gpe_device = NULL; + if (gpe_device) { + target_gpe_device = acpi_gbl_fadt_gpe_device; + } + + /* Integer is the GPE number in the FADT described GPE blocks */ + + gpe_number = (u32)obj_desc->integer.value; + } else if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { + + /* Package contains a GPE reference and GPE number within a GPE block */ + + if ((obj_desc->package.count < 2) || + ((obj_desc->package.elements[0])->common.type != + ACPI_TYPE_LOCAL_REFERENCE) || + ((obj_desc->package.elements[1])->common.type != + ACPI_TYPE_INTEGER)) { + goto cleanup; + } + + /* Get GPE block reference and decode */ + + target_gpe_device = + obj_desc->package.elements[0]->reference.node; + gpe_number = (u32)obj_desc->package.elements[1]->integer.value; + } else { + /* Unknown type, just ignore it */ + + goto cleanup; + } + + /* Get the gpe_event_info for this GPE */ + + if (gpe_device) { + /* + * Is this GPE within this block? + * + * TRUE if and only if these conditions are true: + * 1) The GPE devices match. + * 2) The GPE index(number) is within the range of the Gpe Block + * associated with the GPE device. + */ + if (gpe_device != target_gpe_device) { + goto cleanup; + } + + gpe_event_info = + acpi_ev_low_get_gpe_info(gpe_number, gpe_block); + } else { + /* gpe_device is NULL, just match the target_device and gpe_number */ + + gpe_event_info = + acpi_ev_get_gpe_event_info(target_gpe_device, gpe_number); + } + + if (gpe_event_info) { + if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { + + /* This GPE can wake the system */ + + gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; + walk_info->count++; + } + } + + cleanup: + acpi_ut_remove_reference(pkg_desc); + return_ACPI_STATUS(AE_OK); +} diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c new file mode 100644 index 000000000000..19a0e513ea48 --- /dev/null +++ b/drivers/acpi/acpica/evgpeutil.c @@ -0,0 +1,337 @@ +/****************************************************************************** + * + * Module Name: evgpeutil - GPE utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2010, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acevents.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evgpeutil") + +/******************************************************************************* + * + * FUNCTION: acpi_ev_walk_gpe_list + * + * PARAMETERS: gpe_walk_callback - Routine called for each GPE block + * Context - Value passed to callback + * + * RETURN: Status + * + * DESCRIPTION: Walk the GPE lists. + * + ******************************************************************************/ +acpi_status +acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + acpi_status status = AE_OK; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_walk_gpe_list); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Walk the interrupt level descriptor list */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + + /* Walk all Gpe Blocks attached to this interrupt level */ + + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + + /* One callback per GPE block */ + + status = + gpe_walk_callback(gpe_xrupt_info, gpe_block, + context); + if (ACPI_FAILURE(status)) { + if (status == AE_CTRL_END) { /* Callback abort */ + status = AE_OK; + } + goto unlock_and_exit; + } + + gpe_block = gpe_block->next; + } + + gpe_xrupt_info = gpe_xrupt_info->next; + } + + unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_valid_gpe_event + * + * PARAMETERS: gpe_event_info - Info for this GPE + * + * RETURN: TRUE if the gpe_event is valid + * + * DESCRIPTION: Validate a GPE event. DO NOT CALL FROM INTERRUPT LEVEL. + * Should be called only when the GPE lists are semaphore locked + * and not subject to change. + * + ******************************************************************************/ + +u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info) +{ + struct acpi_gpe_xrupt_info *gpe_xrupt_block; + struct acpi_gpe_block_info *gpe_block; + + ACPI_FUNCTION_ENTRY(); + + /* No need for spin lock since we are not changing any list elements */ + + /* Walk the GPE interrupt levels */ + + gpe_xrupt_block = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_block) { + gpe_block = gpe_xrupt_block->gpe_block_list_head; + + /* Walk the GPE blocks on this interrupt level */ + + while (gpe_block) { + if ((&gpe_block->event_info[0] <= gpe_event_info) && + (&gpe_block->event_info[gpe_block->gpe_count] > + gpe_event_info)) { + return (TRUE); + } + + gpe_block = gpe_block->next; + } + + gpe_xrupt_block = gpe_xrupt_block->next; + } + + return (FALSE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_get_gpe_xrupt_block + * + * PARAMETERS: interrupt_number - Interrupt for a GPE block + * + * RETURN: A GPE interrupt block + * + * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt + * block per unique interrupt level used for GPEs. Should be + * called only when the GPE lists are semaphore locked and not + * subject to change. + * + ******************************************************************************/ + +struct acpi_gpe_xrupt_info *acpi_ev_get_gpe_xrupt_block(u32 interrupt_number) +{ + struct acpi_gpe_xrupt_info *next_gpe_xrupt; + struct acpi_gpe_xrupt_info *gpe_xrupt; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_get_gpe_xrupt_block); + + /* No need for lock since we are not changing any list elements here */ + + next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; + while (next_gpe_xrupt) { + if (next_gpe_xrupt->interrupt_number == interrupt_number) { + return_PTR(next_gpe_xrupt); + } + + next_gpe_xrupt = next_gpe_xrupt->next; + } + + /* Not found, must allocate a new xrupt descriptor */ + + gpe_xrupt = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_xrupt_info)); + if (!gpe_xrupt) { + return_PTR(NULL); + } + + gpe_xrupt->interrupt_number = interrupt_number; + + /* Install new interrupt descriptor with spin lock */ + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + if (acpi_gbl_gpe_xrupt_list_head) { + next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; + while (next_gpe_xrupt->next) { + next_gpe_xrupt = next_gpe_xrupt->next; + } + + next_gpe_xrupt->next = gpe_xrupt; + gpe_xrupt->previous = next_gpe_xrupt; + } else { + acpi_gbl_gpe_xrupt_list_head = gpe_xrupt; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + /* Install new interrupt handler if not SCI_INT */ + + if (interrupt_number != acpi_gbl_FADT.sci_interrupt) { + status = acpi_os_install_interrupt_handler(interrupt_number, + acpi_ev_gpe_xrupt_handler, + gpe_xrupt); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not install GPE interrupt handler at level 0x%X", + interrupt_number)); + return_PTR(NULL); + } + } + + return_PTR(gpe_xrupt); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_delete_gpe_xrupt + * + * PARAMETERS: gpe_xrupt - A GPE interrupt info block + * + * RETURN: Status + * + * DESCRIPTION: Remove and free a gpe_xrupt block. Remove an associated + * interrupt handler if not the SCI interrupt. + * + ******************************************************************************/ + +acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt) +{ + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_delete_gpe_xrupt); + + /* We never want to remove the SCI interrupt handler */ + + if (gpe_xrupt->interrupt_number == acpi_gbl_FADT.sci_interrupt) { + gpe_xrupt->gpe_block_list_head = NULL; + return_ACPI_STATUS(AE_OK); + } + + /* Disable this interrupt */ + + status = + acpi_os_remove_interrupt_handler(gpe_xrupt->interrupt_number, + acpi_ev_gpe_xrupt_handler); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Unlink the interrupt block with lock */ + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + if (gpe_xrupt->previous) { + gpe_xrupt->previous->next = gpe_xrupt->next; + } else { + /* No previous, update list head */ + + acpi_gbl_gpe_xrupt_list_head = gpe_xrupt->next; + } + + if (gpe_xrupt->next) { + gpe_xrupt->next->previous = gpe_xrupt->previous; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + /* Free the block */ + + ACPI_FREE(gpe_xrupt); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_delete_gpe_handlers + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Delete all Handler objects found in the GPE data structs. + * Used only prior to termination. + * + ******************************************************************************/ + +acpi_status +acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context) +{ + struct acpi_gpe_event_info *gpe_event_info; + u32 i; + u32 j; + + ACPI_FUNCTION_TRACE(ev_delete_gpe_handlers); + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + + /* Now look at the individual GPEs in this byte register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + gpe_event_info = &gpe_block->event_info[((acpi_size) i * + ACPI_GPE_REGISTER_WIDTH) + + j]; + + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + ACPI_GPE_DISPATCH_HANDLER) { + ACPI_FREE(gpe_event_info->dispatch.handler); + gpe_event_info->dispatch.handler = NULL; + gpe_event_info->flags &= + ~ACPI_GPE_DISPATCH_MASK; + } + } + } + + return_ACPI_STATUS(AE_OK); +} diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index 9a3cb7045a32..df0aea9a8cfd 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -590,7 +590,7 @@ void acpi_ev_terminate(void) status = acpi_disable_event(i, 0); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, - "Could not disable fixed event %d", + "Could not disable fixed event %u", (u32) i)); } } diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index b40757955f9b..cc825023012a 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -142,7 +142,7 @@ acpi_install_fixed_event_handler(u32 event, if (ACPI_SUCCESS(status)) status = acpi_enable_event(event, 0); if (ACPI_FAILURE(status)) { - ACPI_WARNING((AE_INFO, "Could not enable fixed event %X", + ACPI_WARNING((AE_INFO, "Could not enable fixed event 0x%X", event)); /* Remove the handler */ @@ -203,7 +203,7 @@ acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler) if (ACPI_FAILURE(status)) { ACPI_WARNING((AE_INFO, - "Could not write to fixed event enable register %X", + "Could not write to fixed event enable register 0x%X", event)); } else { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabled fixed event %X\n", @@ -682,14 +682,13 @@ acpi_install_gpe_handler(acpi_handle gpe_device, /* Parameter validation */ - if ((!address) || (type > ACPI_GPE_XRUPT_TYPE_MASK)) { - status = AE_BAD_PARAMETER; - goto exit; + if ((!address) || (type & ~ACPI_GPE_XRUPT_TYPE_MASK)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); } status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); if (ACPI_FAILURE(status)) { - goto exit; + return_ACPI_STATUS(status); } /* Ensure that we have a valid GPE number */ @@ -720,6 +719,13 @@ acpi_install_gpe_handler(acpi_handle gpe_device, handler->context = context; handler->method_node = gpe_event_info->dispatch.method_node; + /* Disable the GPE before installing the handler */ + + status = acpi_ev_disable_gpe(gpe_event_info); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + /* Install the handler */ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); @@ -733,12 +739,8 @@ acpi_install_gpe_handler(acpi_handle gpe_device, acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - unlock_and_exit: +unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - exit: - if (ACPI_FAILURE(status)) - ACPI_EXCEPTION((AE_INFO, status, - "Installing notify handler failed")); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 5ff32c78ea2d..7c7bbb4d402c 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -203,21 +203,26 @@ ACPI_EXPORT_SYMBOL(acpi_enable_event) * * FUNCTION: acpi_set_gpe * - * PARAMETERS: gpe_device - Parent GPE Device + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 * gpe_number - GPE level within the GPE block - * action - Enable or disable - * Called from ISR or not + * action - ACPI_GPE_ENABLE or ACPI_GPE_DISABLE * * RETURN: Status * - * DESCRIPTION: Enable or disable an ACPI event (general purpose) + * DESCRIPTION: Enable or disable an individual GPE. This function bypasses + * the reference count mechanism used in the acpi_enable_gpe and + * acpi_disable_gpe interfaces -- and should be used with care. + * + * Note: Typically used to disable a runtime GPE for short period of time, + * then re-enable it, without disturbing the existing reference counts. This + * is useful, for example, in the Embedded Controller (EC) driver. * ******************************************************************************/ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action) { - acpi_status status = AE_OK; - acpi_cpu_flags flags; struct acpi_gpe_event_info *gpe_event_info; + acpi_status status; + acpi_cpu_flags flags; ACPI_FUNCTION_TRACE(acpi_set_gpe); @@ -243,7 +248,6 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action) break; default: - ACPI_ERROR((AE_INFO, "Invalid action\n")); status = AE_BAD_PARAMETER; break; } @@ -259,25 +263,31 @@ ACPI_EXPORT_SYMBOL(acpi_set_gpe) * * FUNCTION: acpi_enable_gpe * - * PARAMETERS: gpe_device - Parent GPE Device + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 * gpe_number - GPE level within the GPE block - * type - Purpose the GPE will be used for + * gpe_type - ACPI_GPE_TYPE_RUNTIME or ACPI_GPE_TYPE_WAKE + * or both * * RETURN: Status * - * DESCRIPTION: Take a reference to a GPE and enable it if necessary + * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is + * hardware-enabled (for runtime GPEs), or the GPE register mask + * is updated (for wake GPEs). * ******************************************************************************/ -acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type) { acpi_status status = AE_OK; - acpi_cpu_flags flags; struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; ACPI_FUNCTION_TRACE(acpi_enable_gpe); - if (type & ~ACPI_GPE_TYPE_WAKE_RUN) + /* Parameter validation */ + + if (!gpe_type || (gpe_type & ~ACPI_GPE_TYPE_WAKE_RUN)) { return_ACPI_STATUS(AE_BAD_PARAMETER); + } flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); @@ -289,26 +299,43 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) goto unlock_and_exit; } - if (type & ACPI_GPE_TYPE_RUNTIME) { - if (++gpe_event_info->runtime_count == 1) { + if (gpe_type & ACPI_GPE_TYPE_RUNTIME) { + if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) { + status = AE_LIMIT; /* Too many references */ + goto unlock_and_exit; + } + + gpe_event_info->runtime_count++; + if (gpe_event_info->runtime_count == 1) { status = acpi_ev_enable_gpe(gpe_event_info); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { gpe_event_info->runtime_count--; + goto unlock_and_exit; + } } } - if (type & ACPI_GPE_TYPE_WAKE) { + if (gpe_type & ACPI_GPE_TYPE_WAKE) { + /* The GPE must have the ability to wake the system */ + if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { - status = AE_BAD_PARAMETER; + status = AE_TYPE; + goto unlock_and_exit; + } + + if (gpe_event_info->wakeup_count == ACPI_UINT8_MAX) { + status = AE_LIMIT; /* Too many references */ goto unlock_and_exit; } /* - * Wake-up GPEs are only enabled right prior to putting the - * system into a sleep state. + * Update the enable mask on the first wakeup reference. Wake GPEs + * are only hardware-enabled just before sleeping. */ - if (++gpe_event_info->wakeup_count == 1) - acpi_ev_update_gpe_enable_masks(gpe_event_info); + gpe_event_info->wakeup_count++; + if (gpe_event_info->wakeup_count == 1) { + (void)acpi_ev_update_gpe_enable_masks(gpe_event_info); + } } unlock_and_exit: @@ -321,27 +348,34 @@ ACPI_EXPORT_SYMBOL(acpi_enable_gpe) * * FUNCTION: acpi_disable_gpe * - * PARAMETERS: gpe_device - Parent GPE Device + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 * gpe_number - GPE level within the GPE block - * type - Purpose the GPE won't be used for any more + * gpe_type - ACPI_GPE_TYPE_RUNTIME or ACPI_GPE_TYPE_WAKE + * or both * * RETURN: Status * - * DESCRIPTION: Release a reference to a GPE and disable it if necessary + * DESCRIPTION: Remove a reference to a GPE. When the last reference is + * removed, only then is the GPE disabled (for runtime GPEs), or + * the GPE mask bit disabled (for wake GPEs) * ******************************************************************************/ -acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) +acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type) { acpi_status status = AE_OK; - acpi_cpu_flags flags; struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; ACPI_FUNCTION_TRACE(acpi_disable_gpe); - if (type & ~ACPI_GPE_TYPE_WAKE_RUN) + /* Parameter validation */ + + if (!gpe_type || (gpe_type & ~ACPI_GPE_TYPE_WAKE_RUN)) { return_ACPI_STATUS(AE_BAD_PARAMETER); + } flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + /* Ensure that we have a valid GPE number */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); @@ -350,18 +384,39 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) goto unlock_and_exit; } - if ((type & ACPI_GPE_TYPE_RUNTIME) && gpe_event_info->runtime_count) { - if (--gpe_event_info->runtime_count == 0) + /* Hardware-disable a runtime GPE on removal of the last reference */ + + if (gpe_type & ACPI_GPE_TYPE_RUNTIME) { + if (!gpe_event_info->runtime_count) { + status = AE_LIMIT; /* There are no references to remove */ + goto unlock_and_exit; + } + + gpe_event_info->runtime_count--; + if (!gpe_event_info->runtime_count) { status = acpi_ev_disable_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) { + gpe_event_info->runtime_count++; + goto unlock_and_exit; + } + } } - if ((type & ACPI_GPE_TYPE_WAKE) && gpe_event_info->wakeup_count) { - /* - * Wake-up GPEs are not enabled after leaving system sleep - * states, so we don't need to disable them here. - */ - if (--gpe_event_info->wakeup_count == 0) - acpi_ev_update_gpe_enable_masks(gpe_event_info); + /* + * Update masks for wake GPE on removal of the last reference. + * No need to hardware-disable wake GPEs here, they are not currently + * enabled. + */ + if (gpe_type & ACPI_GPE_TYPE_WAKE) { + if (!gpe_event_info->wakeup_count) { + status = AE_LIMIT; /* There are no references to remove */ + goto unlock_and_exit; + } + + gpe_event_info->wakeup_count--; + if (!gpe_event_info->wakeup_count) { + (void)acpi_ev_update_gpe_enable_masks(gpe_event_info); + } } unlock_and_exit: @@ -465,30 +520,23 @@ ACPI_EXPORT_SYMBOL(acpi_clear_event) * * FUNCTION: acpi_clear_gpe * - * PARAMETERS: gpe_device - Parent GPE Device + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 * gpe_number - GPE level within the GPE block - * Flags - Called from an ISR or not * * RETURN: Status * * DESCRIPTION: Clear an ACPI event (general purpose) * ******************************************************************************/ -acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) +acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number) { acpi_status status = AE_OK; struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; ACPI_FUNCTION_TRACE(acpi_clear_gpe); - /* Use semaphore lock if not executing at interrupt level */ - - if (flags & ACPI_NOT_ISR) { - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - } + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ @@ -501,9 +549,7 @@ acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) status = acpi_hw_clear_gpe(gpe_event_info); unlock_and_exit: - if (flags & ACPI_NOT_ISR) { - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -569,9 +615,8 @@ ACPI_EXPORT_SYMBOL(acpi_get_event_status) * * FUNCTION: acpi_get_gpe_status * - * PARAMETERS: gpe_device - Parent GPE Device + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 * gpe_number - GPE level within the GPE block - * Flags - Called from an ISR or not * event_status - Where the current status of the event will * be returned * @@ -582,21 +627,15 @@ ACPI_EXPORT_SYMBOL(acpi_get_event_status) ******************************************************************************/ acpi_status acpi_get_gpe_status(acpi_handle gpe_device, - u32 gpe_number, u32 flags, acpi_event_status * event_status) + u32 gpe_number, acpi_event_status *event_status) { acpi_status status = AE_OK; struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; ACPI_FUNCTION_TRACE(acpi_get_gpe_status); - /* Use semaphore lock if not executing at interrupt level */ - - if (flags & ACPI_NOT_ISR) { - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - } + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ @@ -614,9 +653,7 @@ acpi_get_gpe_status(acpi_handle gpe_device, *event_status |= ACPI_EVENT_FLAG_HANDLE; unlock_and_exit: - if (flags & ACPI_NOT_ISR) { - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -673,20 +710,15 @@ acpi_install_gpe_block(acpi_handle gpe_device, goto unlock_and_exit; } - /* Run the _PRW methods and enable the GPEs */ - - status = acpi_ev_initialize_gpe_block(node, gpe_block); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - - /* Get the device_object attached to the node */ + /* Install block in the device_object attached to the node */ obj_desc = acpi_ns_get_attached_object(node); if (!obj_desc) { - /* No object, create a new one */ - + /* + * No object, create a new one (Device nodes do not always have + * an attached object) + */ obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_DEVICE); if (!obj_desc) { status = AE_NO_MEMORY; @@ -705,10 +737,14 @@ acpi_install_gpe_block(acpi_handle gpe_device, } } - /* Install the GPE block in the device_object */ + /* Now install the GPE block in the device_object */ obj_desc->device.gpe_block = gpe_block; + /* Run the _PRW methods and enable the runtime GPEs in the new block */ + + status = acpi_ev_initialize_gpe_block(node, gpe_block); + unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_ACPI_STATUS(status); @@ -839,8 +875,7 @@ acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Increment Index by the number of GPEs in this block */ - info->next_block_base_index += - (gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH); + info->next_block_base_index += gpe_block->gpe_count; if (info->index < info->next_block_base_index) { /* diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 7e8b3bedc376..008621c5ad85 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -82,8 +82,9 @@ acpi_ex_add_table(u32 table_index, struct acpi_namespace_node *parent_node, union acpi_operand_object **ddb_handle) { - acpi_status status; union acpi_operand_object *obj_desc; + acpi_status status; + acpi_owner_id owner_id; ACPI_FUNCTION_TRACE(ex_add_table); @@ -119,7 +120,14 @@ acpi_ex_add_table(u32 table_index, acpi_ns_exec_module_code_list(); acpi_ex_enter_interpreter(); - return_ACPI_STATUS(status); + /* Update GPEs for any new _PRW or _Lxx/_Exx methods. Ignore errors */ + + status = acpi_tb_get_owner_id(table_index, &owner_id); + if (ACPI_SUCCESS(status)) { + acpi_ev_update_gpes(owner_id); + } + + return_ACPI_STATUS(AE_OK); } /******************************************************************************* @@ -248,10 +256,8 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, status = acpi_get_table_by_index(table_index, &table); if (ACPI_SUCCESS(status)) { - ACPI_INFO((AE_INFO, - "Dynamic OEM Table Load - [%.4s] OemId [%.6s] OemTableId [%.8s]", - table->signature, table->oem_id, - table->oem_table_id)); + ACPI_INFO((AE_INFO, "Dynamic OEM Table Load:")); + acpi_tb_print_table_header(0, table); } /* Invoke table handler if present */ @@ -525,6 +531,9 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, return_ACPI_STATUS(status); } + ACPI_INFO((AE_INFO, "Dynamic OEM Table Load:")); + acpi_tb_print_table_header(0, table_desc.pointer); + /* Remove the reference by added by acpi_ex_store above */ acpi_ut_remove_reference(ddb_handle); diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index bda7aed0404b..b73bc50c5b76 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -650,7 +650,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, default: ACPI_ERROR((AE_INFO, - "Bad destination type during conversion: %X", + "Bad destination type during conversion: 0x%X", destination_type)); status = AE_AML_INTERNAL; break; @@ -665,7 +665,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, default: ACPI_ERROR((AE_INFO, - "Unknown Target type ID 0x%X AmlOpcode %X DestType %s", + "Unknown Target type ID 0x%X AmlOpcode 0x%X DestType %s", GET_CURRENT_ARG_TYPE(walk_state->op_info-> runtime_args), walk_state->opcode, diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index 0aa57d938698..3c61b48c73f5 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -306,12 +306,12 @@ acpi_ex_create_region(u8 * aml_start, */ if ((region_space >= ACPI_NUM_PREDEFINED_REGIONS) && (region_space < ACPI_USER_REGION_BEGIN)) { - ACPI_ERROR((AE_INFO, "Invalid AddressSpace type %X", + ACPI_ERROR((AE_INFO, "Invalid AddressSpace type 0x%X", region_space)); return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); } - ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Region Type - %s (%X)\n", + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Region Type - %s (0x%X)\n", acpi_ut_get_region_name(region_space), region_space)); /* Create the region descriptor */ diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c new file mode 100644 index 000000000000..be8c98b480d7 --- /dev/null +++ b/drivers/acpi/acpica/exdebug.c @@ -0,0 +1,261 @@ +/****************************************************************************** + * + * Module Name: exdebug - Support for stores to the AML Debug Object + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2010, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exdebug") + +#ifndef ACPI_NO_ERROR_MESSAGES +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_debug_object + * + * PARAMETERS: source_desc - Object to be output to "Debug Object" + * Level - Indentation level (used for packages) + * Index - Current package element, zero if not pkg + * + * RETURN: None + * + * DESCRIPTION: Handles stores to the AML Debug Object. For example: + * Store(INT1, Debug) + * + * This function is not compiled if ACPI_NO_ERROR_MESSAGES is set. + * + * This function is only enabled if acpi_gbl_enable_aml_debug_object is set, or + * if ACPI_LV_DEBUG_OBJECT is set in the acpi_dbg_level. Thus, in the normal + * operational case, stores to the debug object are ignored but can be easily + * enabled if necessary. + * + ******************************************************************************/ +void +acpi_ex_do_debug_object(union acpi_operand_object *source_desc, + u32 level, u32 index) +{ + u32 i; + + ACPI_FUNCTION_TRACE_PTR(ex_do_debug_object, source_desc); + + /* Output must be enabled via the debug_object global or the dbg_level */ + + if (!acpi_gbl_enable_aml_debug_object && + !(acpi_dbg_level & ACPI_LV_DEBUG_OBJECT)) { + return_VOID; + } + + /* + * Print line header as long as we are not in the middle of an + * object display + */ + if (!((level > 0) && index == 0)) { + acpi_os_printf("[ACPI Debug] %*s", level, " "); + } + + /* Display the index for package output only */ + + if (index > 0) { + acpi_os_printf("(%.2u) ", index - 1); + } + + if (!source_desc) { + acpi_os_printf("[Null Object]\n"); + return_VOID; + } + + if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf("%s ", + acpi_ut_get_object_type_name(source_desc)); + + if (!acpi_ut_valid_internal_object(source_desc)) { + acpi_os_printf("%p, Invalid Internal Object!\n", + source_desc); + return_VOID; + } + } else if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == + ACPI_DESC_TYPE_NAMED) { + acpi_os_printf("%s: %p\n", + acpi_ut_get_type_name(((struct + acpi_namespace_node *) + source_desc)->type), + source_desc); + return_VOID; + } else { + return_VOID; + } + + /* source_desc is of type ACPI_DESC_TYPE_OPERAND */ + + switch (source_desc->common.type) { + case ACPI_TYPE_INTEGER: + + /* Output correct integer width */ + + if (acpi_gbl_integer_byte_width == 4) { + acpi_os_printf("0x%8.8X\n", + (u32)source_desc->integer.value); + } else { + acpi_os_printf("0x%8.8X%8.8X\n", + ACPI_FORMAT_UINT64(source_desc->integer. + value)); + } + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf("[0x%.2X]\n", (u32)source_desc->buffer.length); + acpi_ut_dump_buffer2(source_desc->buffer.pointer, + (source_desc->buffer.length < 256) ? + source_desc->buffer.length : 256, + DB_BYTE_DISPLAY); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("[0x%.2X] \"%s\"\n", + source_desc->string.length, + source_desc->string.pointer); + break; + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf("[Contains 0x%.2X Elements]\n", + source_desc->package.count); + + /* Output the entire contents of the package */ + + for (i = 0; i < source_desc->package.count; i++) { + acpi_ex_do_debug_object(source_desc->package. + elements[i], level + 4, i + 1); + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("[%s] ", + acpi_ut_get_reference_name(source_desc)); + + /* Decode the reference */ + + switch (source_desc->reference.class) { + case ACPI_REFCLASS_INDEX: + + acpi_os_printf("0x%X\n", source_desc->reference.value); + break; + + case ACPI_REFCLASS_TABLE: + + /* Case for ddb_handle */ + + acpi_os_printf("Table Index 0x%X\n", + source_desc->reference.value); + return; + + default: + break; + } + + acpi_os_printf(" "); + + /* Check for valid node first, then valid object */ + + if (source_desc->reference.node) { + if (ACPI_GET_DESCRIPTOR_TYPE + (source_desc->reference.node) != + ACPI_DESC_TYPE_NAMED) { + acpi_os_printf + (" %p - Not a valid namespace node\n", + source_desc->reference.node); + } else { + acpi_os_printf("Node %p [%4.4s] ", + source_desc->reference.node, + (source_desc->reference.node)-> + name.ascii); + + switch ((source_desc->reference.node)->type) { + + /* These types have no attached object */ + + case ACPI_TYPE_DEVICE: + acpi_os_printf("Device\n"); + break; + + case ACPI_TYPE_THERMAL: + acpi_os_printf("Thermal Zone\n"); + break; + + default: + acpi_ex_do_debug_object((source_desc-> + reference. + node)->object, + level + 4, 0); + break; + } + } + } else if (source_desc->reference.object) { + if (ACPI_GET_DESCRIPTOR_TYPE + (source_desc->reference.object) == + ACPI_DESC_TYPE_NAMED) { + acpi_ex_do_debug_object(((struct + acpi_namespace_node *) + source_desc->reference. + object)->object, + level + 4, 0); + } else { + acpi_ex_do_debug_object(source_desc->reference. + object, level + 4, 0); + } + } + break; + + default: + + acpi_os_printf("%p\n", source_desc); + break; + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "\n")); + return_VOID; +} +#endif diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index 6c79fecbee42..f17d2ff0031b 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -281,7 +281,7 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, if (source_desc->buffer.length < length) { ACPI_ERROR((AE_INFO, - "SMBus or IPMI write requires Buffer of length %X, found length %X", + "SMBus or IPMI write requires Buffer of length %u, found length %u", length, source_desc->buffer.length)); return_ACPI_STATUS(AE_AML_BUFFER_LIMIT); diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index f68a216168be..a6dc26f0b3be 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -94,7 +94,7 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, /* We must have a valid region */ if (rgn_desc->common.type != ACPI_TYPE_REGION) { - ACPI_ERROR((AE_INFO, "Needed Region, found type %X (%s)", + ACPI_ERROR((AE_INFO, "Needed Region, found type 0x%X (%s)", rgn_desc->common.type, acpi_ut_get_object_type_name(rgn_desc))); @@ -175,7 +175,7 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, * byte, and a field with Dword access specified. */ ACPI_ERROR((AE_INFO, - "Field [%4.4s] access width (%d bytes) too large for region [%4.4s] (length %X)", + "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)", acpi_ut_get_node_name(obj_desc-> common_field.node), obj_desc->common_field.access_byte_width, @@ -189,7 +189,7 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, * exceeds region length, indicate an error */ ACPI_ERROR((AE_INFO, - "Field [%4.4s] Base+Offset+Width %X+%X+%X is beyond end of region [%4.4s] (length %X)", + "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)", acpi_ut_get_node_name(obj_desc->common_field.node), obj_desc->common_field.base_byte_offset, field_datum_byte_offset, @@ -281,13 +281,13 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc, if (ACPI_FAILURE(status)) { if (status == AE_NOT_IMPLEMENTED) { ACPI_ERROR((AE_INFO, - "Region %s(%X) not implemented", + "Region %s(0x%X) not implemented", acpi_ut_get_region_name(rgn_desc->region. space_id), rgn_desc->region.space_id)); } else if (status == AE_NOT_EXIST) { ACPI_ERROR((AE_INFO, - "Region %s(%X) has no handler", + "Region %s(0x%X) has no handler", acpi_ut_get_region_name(rgn_desc->region. space_id), rgn_desc->region.space_id)); @@ -525,7 +525,7 @@ acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, default: - ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %X", + ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %u", obj_desc->common.type)); status = AE_AML_INTERNAL; break; @@ -630,7 +630,7 @@ acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc, default: ACPI_ERROR((AE_INFO, - "Unknown UpdateRule value: %X", + "Unknown UpdateRule value: 0x%X", (obj_desc->common_field. field_flags & AML_FIELD_UPDATE_RULE_MASK))); @@ -689,7 +689,7 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, if (buffer_length < ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) { ACPI_ERROR((AE_INFO, - "Field size %X (bits) is too large for buffer (%X)", + "Field size %u (bits) is too large for buffer (%u)", obj_desc->common_field.bit_length, buffer_length)); return_ACPI_STATUS(AE_BUFFER_OVERFLOW); diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c index c5bb1eeed2df..95db4be0877b 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -99,7 +99,7 @@ acpi_ex_get_object_reference(union acpi_operand_object *obj_desc, default: - ACPI_ERROR((AE_INFO, "Unknown Reference Class %2.2X", + ACPI_ERROR((AE_INFO, "Unknown Reference Class 0x%2.2X", obj_desc->reference.class)); return_ACPI_STATUS(AE_AML_INTERNAL); } @@ -115,7 +115,7 @@ acpi_ex_get_object_reference(union acpi_operand_object *obj_desc, default: - ACPI_ERROR((AE_INFO, "Invalid descriptor type %X", + ACPI_ERROR((AE_INFO, "Invalid descriptor type 0x%X", ACPI_GET_DESCRIPTOR_TYPE(obj_desc))); return_ACPI_STATUS(AE_TYPE); } @@ -276,7 +276,7 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0, break; default: - ACPI_ERROR((AE_INFO, "Invalid object type: %X", + ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", operand0->common.type)); status = AE_AML_INTERNAL; } @@ -378,7 +378,7 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0, /* Invalid object type, should not happen here */ - ACPI_ERROR((AE_INFO, "Invalid object type: %X", + ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", operand0->common.type)); status = AE_AML_INTERNAL; goto cleanup; diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index 7116bc86494d..f73be97043c0 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -85,10 +85,10 @@ void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc) (obj_desc->mutex.prev)->mutex.next = obj_desc->mutex.next; /* - * Migrate the previous sync level associated with this mutex to the - * previous mutex on the list so that it may be preserved. This handles - * the case where several mutexes have been acquired at the same level, - * but are not released in opposite order. + * Migrate the previous sync level associated with this mutex to + * the previous mutex on the list so that it may be preserved. + * This handles the case where several mutexes have been acquired + * at the same level, but are not released in opposite order. */ (obj_desc->mutex.prev)->mutex.original_sync_level = obj_desc->mutex.original_sync_level; @@ -101,8 +101,8 @@ void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc) * * FUNCTION: acpi_ex_link_mutex * - * PARAMETERS: obj_desc - The mutex to be linked - * Thread - Current executing thread object + * PARAMETERS: obj_desc - The mutex to be linked + * Thread - Current executing thread object * * RETURN: None * @@ -138,9 +138,9 @@ acpi_ex_link_mutex(union acpi_operand_object *obj_desc, * * FUNCTION: acpi_ex_acquire_mutex_object * - * PARAMETERS: time_desc - Timeout in milliseconds + * PARAMETERS: Timeout - Timeout in milliseconds * obj_desc - Mutex object - * Thread - Current thread state + * thread_id - Current thread state * * RETURN: Status * @@ -234,7 +234,7 @@ acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, return_ACPI_STATUS(AE_BAD_PARAMETER); } - /* Must have a valid thread ID */ + /* Must have a valid thread state struct */ if (!walk_state->thread) { ACPI_ERROR((AE_INFO, @@ -249,7 +249,7 @@ acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, */ if (walk_state->thread->current_sync_level > obj_desc->mutex.sync_level) { ACPI_ERROR((AE_INFO, - "Cannot acquire Mutex [%4.4s], current SyncLevel is too large (%d)", + "Cannot acquire Mutex [%4.4s], current SyncLevel is too large (%u)", acpi_ut_get_node_name(obj_desc->mutex.node), walk_state->thread->current_sync_level)); return_ACPI_STATUS(AE_AML_MUTEX_ORDER); @@ -359,6 +359,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, { acpi_status status = AE_OK; u8 previous_sync_level; + struct acpi_thread_state *owner_thread; ACPI_FUNCTION_TRACE(ex_release_mutex); @@ -366,9 +367,11 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_BAD_PARAMETER); } + owner_thread = obj_desc->mutex.owner_thread; + /* The mutex must have been previously acquired in order to release it */ - if (!obj_desc->mutex.owner_thread) { + if (!owner_thread) { ACPI_ERROR((AE_INFO, "Cannot release Mutex [%4.4s], not acquired", acpi_ut_get_node_name(obj_desc->mutex.node))); @@ -387,16 +390,13 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, * The Mutex is owned, but this thread must be the owner. * Special case for Global Lock, any thread can release */ - if ((obj_desc->mutex.owner_thread->thread_id != - walk_state->thread->thread_id) - && (obj_desc != acpi_gbl_global_lock_mutex)) { + if ((owner_thread->thread_id != walk_state->thread->thread_id) && + (obj_desc != acpi_gbl_global_lock_mutex)) { ACPI_ERROR((AE_INFO, "Thread %p cannot release Mutex [%4.4s] acquired by thread %p", ACPI_CAST_PTR(void, walk_state->thread->thread_id), acpi_ut_get_node_name(obj_desc->mutex.node), - ACPI_CAST_PTR(void, - obj_desc->mutex.owner_thread-> - thread_id))); + ACPI_CAST_PTR(void, owner_thread->thread_id))); return_ACPI_STATUS(AE_AML_NOT_OWNER); } @@ -407,10 +407,9 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, * different level can only mean that the mutex ordering rule is being * violated. This behavior is clarified in ACPI 4.0 specification. */ - if (obj_desc->mutex.sync_level != - walk_state->thread->current_sync_level) { + if (obj_desc->mutex.sync_level != owner_thread->current_sync_level) { ACPI_ERROR((AE_INFO, - "Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %d current %d", + "Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %u current %u", acpi_ut_get_node_name(obj_desc->mutex.node), obj_desc->mutex.sync_level, walk_state->thread->current_sync_level)); @@ -423,7 +422,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, * acquired, but are not released in reverse order. */ previous_sync_level = - walk_state->thread->acquired_mutex_list->mutex.original_sync_level; + owner_thread->acquired_mutex_list->mutex.original_sync_level; status = acpi_ex_release_mutex_object(obj_desc); if (ACPI_FAILURE(status)) { @@ -434,8 +433,9 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, /* Restore the previous sync_level */ - walk_state->thread->current_sync_level = previous_sync_level; + owner_thread->current_sync_level = previous_sync_level; } + return_ACPI_STATUS(status); } @@ -443,7 +443,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, * * FUNCTION: acpi_ex_release_all_mutexes * - * PARAMETERS: Thread - Current executing thread object + * PARAMETERS: Thread - Current executing thread object * * RETURN: Status * diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c index 679f308c5a89..d11e539ef763 100644 --- a/drivers/acpi/acpica/exnames.c +++ b/drivers/acpi/acpica/exnames.c @@ -102,7 +102,7 @@ static char *acpi_ex_allocate_name_string(u32 prefix_count, u32 num_name_segs) name_string = ACPI_ALLOCATE(size_needed); if (!name_string) { ACPI_ERROR((AE_INFO, - "Could not allocate size %d", size_needed)); + "Could not allocate size %u", size_needed)); return_PTR(NULL); } @@ -216,7 +216,7 @@ static acpi_status acpi_ex_name_segment(u8 ** in_aml_address, char *name_string) */ status = AE_AML_BAD_NAME; ACPI_ERROR((AE_INFO, - "Bad character %02x in name, at %p", + "Bad character 0x%02x in name, at %p", *aml_address, aml_address)); } diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index 99adbab5acbf..84e4d185aa25 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -110,7 +110,7 @@ acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state) default: /* Unknown opcode */ - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; break; @@ -173,7 +173,7 @@ acpi_status acpi_ex_opcode_1A_0T_0R(struct acpi_walk_state *walk_state) case AML_SLEEP_OP: /* Sleep (msec_time) */ - status = acpi_ex_system_do_suspend(operand[0]->integer.value); + status = acpi_ex_system_do_sleep(operand[0]->integer.value); break; case AML_STALL_OP: /* Stall (usec_time) */ @@ -189,7 +189,7 @@ acpi_status acpi_ex_opcode_1A_0T_0R(struct acpi_walk_state *walk_state) default: /* Unknown opcode */ - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; break; @@ -229,7 +229,7 @@ acpi_status acpi_ex_opcode_1A_1T_0R(struct acpi_walk_state *walk_state) default: /* Unknown opcode */ - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; @@ -399,7 +399,7 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state) if (digit > 0) { ACPI_ERROR((AE_INFO, - "Integer too large to convert to BCD: %8.8X%8.8X", + "Integer too large to convert to BCD: 0x%8.8X%8.8X", ACPI_FORMAT_UINT64(operand[0]-> integer.value))); status = AE_AML_NUMERIC_OVERFLOW; @@ -540,7 +540,7 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state) default: /* Unknown opcode */ - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; @@ -979,7 +979,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) default: ACPI_ERROR((AE_INFO, - "Unknown Index TargetType %X in reference object %p", + "Unknown Index TargetType 0x%X in reference object %p", operand[0]->reference. target_type, operand[0])); status = AE_AML_OPERAND_TYPE; @@ -1007,7 +1007,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) default: ACPI_ERROR((AE_INFO, - "Unknown class in reference(%p) - %2.2X", + "Unknown class in reference(%p) - 0x%2.2X", operand[0], operand[0]->reference.class)); @@ -1019,7 +1019,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) default: - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c index 22841bbbe63c..10e104cf0fb9 100644 --- a/drivers/acpi/acpica/exoparg2.c +++ b/drivers/acpi/acpica/exoparg2.c @@ -119,33 +119,6 @@ acpi_status acpi_ex_opcode_2A_0T_0R(struct acpi_walk_state *walk_state) status = AE_AML_OPERAND_TYPE; break; } -#ifdef ACPI_GPE_NOTIFY_CHECK - /* - * GPE method wake/notify check. Here, we want to ensure that we - * don't receive any "DeviceWake" Notifies from a GPE _Lxx or _Exx - * GPE method during system runtime. If we do, the GPE is marked - * as "wake-only" and disabled. - * - * 1) Is the Notify() value == device_wake? - * 2) Is this a GPE deferred method? (An _Lxx or _Exx method) - * 3) Did the original GPE happen at system runtime? - * (versus during wake) - * - * If all three cases are true, this is a wake-only GPE that should - * be disabled at runtime. - */ - if (value == 2) { /* device_wake */ - status = - acpi_ev_check_for_wake_only_gpe(walk_state-> - gpe_event_info); - if (ACPI_FAILURE(status)) { - - /* AE_WAKE_ONLY_GPE only error, means ignore this notify */ - - return_ACPI_STATUS(AE_OK) - } - } -#endif /* * Dispatch the notify to the appropriate handler @@ -159,7 +132,7 @@ acpi_status acpi_ex_opcode_2A_0T_0R(struct acpi_walk_state *walk_state) default: - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; } @@ -224,7 +197,7 @@ acpi_status acpi_ex_opcode_2A_2T_1R(struct acpi_walk_state *walk_state) default: - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; @@ -441,7 +414,7 @@ acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state) if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Index (%X%8.8X) is beyond end of object", + "Index (0x%8.8X%8.8X) is beyond end of object", ACPI_FORMAT_UINT64(index))); goto cleanup; } @@ -464,7 +437,7 @@ acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state) default: - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; break; @@ -572,7 +545,7 @@ acpi_status acpi_ex_opcode_2A_0T_1R(struct acpi_walk_state *walk_state) default: - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c index 8bb1012ef44e..7a08d23befcd 100644 --- a/drivers/acpi/acpica/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -119,7 +119,7 @@ acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state) default: - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; @@ -244,7 +244,7 @@ acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state) default: - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c index f256b6a25f2e..4b50730cf9a0 100644 --- a/drivers/acpi/acpica/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -245,7 +245,7 @@ acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state * walk_state) index = operand[5]->integer.value; if (index >= operand[0]->package.count) { ACPI_ERROR((AE_INFO, - "Index (%X%8.8X) beyond package end (%X)", + "Index (0x%8.8X%8.8X) beyond package end (0x%X)", ACPI_FORMAT_UINT64(index), operand[0]->package.count)); status = AE_AML_PACKAGE_LIMIT; @@ -314,7 +314,7 @@ acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state * walk_state) default: - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); status = AE_AML_BAD_OPCODE; goto cleanup; diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index 2fbfe51fb141..25059dace0ad 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -275,7 +275,7 @@ acpi_ex_decode_field_access(union acpi_operand_object *obj_desc, default: /* Invalid field access type */ - ACPI_ERROR((AE_INFO, "Unknown field access type %X", access)); + ACPI_ERROR((AE_INFO, "Unknown field access type 0x%X", access)); return_UINT32(0); } @@ -430,7 +430,7 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) type = acpi_ns_get_type(info->region_node); if (type != ACPI_TYPE_REGION) { ACPI_ERROR((AE_INFO, - "Needed Region, found type %X (%s)", + "Needed Region, found type 0x%X (%s)", type, acpi_ut_get_type_name(type))); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index 486b2e5661b6..531000fc77d2 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -105,7 +105,7 @@ acpi_ex_system_memory_space_handler(u32 function, break; default: - ACPI_ERROR((AE_INFO, "Invalid SystemMemory width %d", + ACPI_ERROR((AE_INFO, "Invalid SystemMemory width %u", bit_width)); return_ACPI_STATUS(AE_AML_OPERAND_VALUE); } @@ -173,7 +173,7 @@ acpi_ex_system_memory_space_handler(u32 function, mem_info->mapped_logical_address = acpi_os_map_memory((acpi_physical_address) address, map_length); if (!mem_info->mapped_logical_address) { ACPI_ERROR((AE_INFO, - "Could not map memory at %8.8X%8.8X, size %X", + "Could not map memory at 0x%8.8X%8.8X, size %u", ACPI_FORMAT_NATIVE_UINT(address), (u32) map_length)); mem_info->mapped_length = 0; @@ -491,8 +491,10 @@ acpi_ex_data_table_space_handler(u32 function, { ACPI_FUNCTION_TRACE(ex_data_table_space_handler); - /* Perform the memory read or write */ - + /* + * Perform the memory read or write. The bit_width was already + * validated. + */ switch (function) { case ACPI_READ: @@ -502,9 +504,14 @@ acpi_ex_data_table_space_handler(u32 function, break; case ACPI_WRITE: + + ACPI_MEMCPY(ACPI_PHYSADDR_TO_PTR(address), + ACPI_CAST_PTR(char, value), ACPI_DIV_8(bit_width)); + break; + default: - return_ACPI_STATUS(AE_SUPPORT); + return_ACPI_STATUS(AE_BAD_PARAMETER); } return_ACPI_STATUS(AE_OK); diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c index fdc1b27999ef..1fa4289a687e 100644 --- a/drivers/acpi/acpica/exresnte.c +++ b/drivers/acpi/acpica/exresnte.c @@ -252,7 +252,7 @@ acpi_ex_resolve_node_to_value(struct acpi_namespace_node **object_ptr, /* No named references are allowed here */ ACPI_ERROR((AE_INFO, - "Unsupported Reference type %X", + "Unsupported Reference type 0x%X", source_desc->reference.class)); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); @@ -264,7 +264,7 @@ acpi_ex_resolve_node_to_value(struct acpi_namespace_node **object_ptr, /* Default case is for unknown types */ ACPI_ERROR((AE_INFO, - "Node %p - Unknown object type %X", + "Node %p - Unknown object type 0x%X", node, entry_type)); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c index fdd6a7079b97..7ca35ea8acea 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -231,7 +231,7 @@ acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr, /* Invalid reference object */ ACPI_ERROR((AE_INFO, - "Unknown TargetType %X in Index/Reference object %p", + "Unknown TargetType 0x%X in Index/Reference object %p", stack_desc->reference.target_type, stack_desc)); status = AE_AML_INTERNAL; @@ -273,8 +273,8 @@ acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr, default: ACPI_ERROR((AE_INFO, - "Unknown Reference type %X in %p", ref_type, - stack_desc)); + "Unknown Reference type 0x%X in %p", + ref_type, stack_desc)); status = AE_AML_INTERNAL; break; } @@ -403,7 +403,8 @@ acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state, if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { - ACPI_ERROR((AE_INFO, "Not a NS node %p [%s]", + ACPI_ERROR((AE_INFO, + "Not a namespace node %p [%s]", node, acpi_ut_get_descriptor_name(node))); return_ACPI_STATUS(AE_AML_INTERNAL); @@ -507,7 +508,7 @@ acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state, default: ACPI_ERROR((AE_INFO, - "Unknown Reference Class %2.2X", + "Unknown Reference Class 0x%2.2X", obj_desc->reference.class)); return_ACPI_STATUS(AE_AML_INTERNAL); } diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index c5ecd615f145..8c97cfd6a0fd 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -153,7 +153,7 @@ acpi_ex_resolve_operands(u16 opcode, arg_types = op_info->runtime_args; if (arg_types == ARGI_INVALID_OPCODE) { - ACPI_ERROR((AE_INFO, "Unknown AML opcode %X", opcode)); + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", opcode)); return_ACPI_STATUS(AE_AML_INTERNAL); } @@ -218,7 +218,7 @@ acpi_ex_resolve_operands(u16 opcode, if (!acpi_ut_valid_object_type(object_type)) { ACPI_ERROR((AE_INFO, - "Bad operand object type [%X]", + "Bad operand object type [0x%X]", object_type)); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); @@ -253,7 +253,7 @@ acpi_ex_resolve_operands(u16 opcode, default: ACPI_ERROR((AE_INFO, - "Unknown Reference Class %2.2X in %p", + "Unknown Reference Class 0x%2.2X in %p", obj_desc->reference.class, obj_desc)); @@ -665,7 +665,7 @@ acpi_ex_resolve_operands(u16 opcode, /* Unknown type */ ACPI_ERROR((AE_INFO, - "Internal - Unknown ARGI (required operand) type %X", + "Internal - Unknown ARGI (required operand) type 0x%X", this_arg_type)); return_ACPI_STATUS(AE_BAD_PARAMETER); diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index 702b9ecfd44b..1624436ba4c5 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -1,4 +1,3 @@ - /****************************************************************************** * * Module Name: exstore - AML Interpreter object store support @@ -53,10 +52,6 @@ ACPI_MODULE_NAME("exstore") /* Local prototypes */ -static void -acpi_ex_do_debug_object(union acpi_operand_object *source_desc, - u32 level, u32 index); - static acpi_status acpi_ex_store_object_to_index(union acpi_operand_object *val_desc, union acpi_operand_object *dest_desc, @@ -64,215 +59,6 @@ acpi_ex_store_object_to_index(union acpi_operand_object *val_desc, /******************************************************************************* * - * FUNCTION: acpi_ex_do_debug_object - * - * PARAMETERS: source_desc - Value to be stored - * Level - Indentation level (used for packages) - * Index - Current package element, zero if not pkg - * - * RETURN: None - * - * DESCRIPTION: Handles stores to the Debug Object. - * - ******************************************************************************/ - -static void -acpi_ex_do_debug_object(union acpi_operand_object *source_desc, - u32 level, u32 index) -{ - u32 i; - - ACPI_FUNCTION_TRACE_PTR(ex_do_debug_object, source_desc); - - /* Print line header as long as we are not in the middle of an object display */ - - if (!((level > 0) && index == 0)) { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "[ACPI Debug] %*s", - level, " ")); - } - - /* Display index for package output only */ - - if (index > 0) { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, - "(%.2u) ", index - 1)); - } - - if (!source_desc) { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "[Null Object]\n")); - return_VOID; - } - - if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_OPERAND) { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "%s ", - acpi_ut_get_object_type_name - (source_desc))); - - if (!acpi_ut_valid_internal_object(source_desc)) { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, - "%p, Invalid Internal Object!\n", - source_desc)); - return_VOID; - } - } else if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == - ACPI_DESC_TYPE_NAMED) { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "%s: %p\n", - acpi_ut_get_type_name(((struct - acpi_namespace_node - *)source_desc)-> - type), - source_desc)); - return_VOID; - } else { - return_VOID; - } - - /* source_desc is of type ACPI_DESC_TYPE_OPERAND */ - - switch (source_desc->common.type) { - case ACPI_TYPE_INTEGER: - - /* Output correct integer width */ - - if (acpi_gbl_integer_byte_width == 4) { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "0x%8.8X\n", - (u32) source_desc->integer. - value)); - } else { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, - "0x%8.8X%8.8X\n", - ACPI_FORMAT_UINT64(source_desc-> - integer. - value))); - } - break; - - case ACPI_TYPE_BUFFER: - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "[0x%.2X]\n", - (u32) source_desc->buffer.length)); - ACPI_DUMP_BUFFER(source_desc->buffer.pointer, - (source_desc->buffer.length < - 256) ? source_desc->buffer.length : 256); - break; - - case ACPI_TYPE_STRING: - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "[0x%.2X] \"%s\"\n", - source_desc->string.length, - source_desc->string.pointer)); - break; - - case ACPI_TYPE_PACKAGE: - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, - "[Contains 0x%.2X Elements]\n", - source_desc->package.count)); - - /* Output the entire contents of the package */ - - for (i = 0; i < source_desc->package.count; i++) { - acpi_ex_do_debug_object(source_desc->package. - elements[i], level + 4, i + 1); - } - break; - - case ACPI_TYPE_LOCAL_REFERENCE: - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "[%s] ", - acpi_ut_get_reference_name(source_desc))); - - /* Decode the reference */ - - switch (source_desc->reference.class) { - case ACPI_REFCLASS_INDEX: - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "0x%X\n", - source_desc->reference.value)); - break; - - case ACPI_REFCLASS_TABLE: - - /* Case for ddb_handle */ - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, - "Table Index 0x%X\n", - source_desc->reference.value)); - return; - - default: - break; - } - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, " ")); - - /* Check for valid node first, then valid object */ - - if (source_desc->reference.node) { - if (ACPI_GET_DESCRIPTOR_TYPE - (source_desc->reference.node) != - ACPI_DESC_TYPE_NAMED) { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, - " %p - Not a valid namespace node\n", - source_desc->reference. - node)); - } else { - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, - "Node %p [%4.4s] ", - source_desc->reference. - node, - (source_desc->reference. - node)->name.ascii)); - - switch ((source_desc->reference.node)->type) { - - /* These types have no attached object */ - - case ACPI_TYPE_DEVICE: - acpi_os_printf("Device\n"); - break; - - case ACPI_TYPE_THERMAL: - acpi_os_printf("Thermal Zone\n"); - break; - - default: - acpi_ex_do_debug_object((source_desc-> - reference. - node)->object, - level + 4, 0); - break; - } - } - } else if (source_desc->reference.object) { - if (ACPI_GET_DESCRIPTOR_TYPE - (source_desc->reference.object) == - ACPI_DESC_TYPE_NAMED) { - acpi_ex_do_debug_object(((struct - acpi_namespace_node *) - source_desc->reference. - object)->object, - level + 4, 0); - } else { - acpi_ex_do_debug_object(source_desc->reference. - object, level + 4, 0); - } - } - break; - - default: - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_DEBUG_OBJECT, "%p\n", - source_desc)); - break; - } - - ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "\n")); - return_VOID; -} - -/******************************************************************************* - * * FUNCTION: acpi_ex_store * * PARAMETERS: *source_desc - Value to be stored @@ -402,12 +188,12 @@ acpi_ex_store(union acpi_operand_object *source_desc, source_desc, acpi_ut_get_object_type_name(source_desc))); - acpi_ex_do_debug_object(source_desc, 0, 0); + ACPI_DEBUG_OBJECT(source_desc, 0, 0); break; default: - ACPI_ERROR((AE_INFO, "Unknown Reference Class %2.2X", + ACPI_ERROR((AE_INFO, "Unknown Reference Class 0x%2.2X", ref_desc->reference.class)); ACPI_DUMP_ENTRY(ref_desc, ACPI_LV_INFO); diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c index e11b6cb42a57..6d32e09327f1 100644 --- a/drivers/acpi/acpica/exsystem.c +++ b/drivers/acpi/acpica/exsystem.c @@ -170,7 +170,7 @@ acpi_status acpi_ex_system_do_stall(u32 how_long) * (ACPI specifies 100 usec as max, but this gives some slack in * order to support existing BIOSs) */ - ACPI_ERROR((AE_INFO, "Time parameter is too large (%d)", + ACPI_ERROR((AE_INFO, "Time parameter is too large (%u)", how_long)); status = AE_AML_OPERAND_VALUE; } else { @@ -182,18 +182,18 @@ acpi_status acpi_ex_system_do_stall(u32 how_long) /******************************************************************************* * - * FUNCTION: acpi_ex_system_do_suspend + * FUNCTION: acpi_ex_system_do_sleep * - * PARAMETERS: how_long - The amount of time to suspend, + * PARAMETERS: how_long - The amount of time to sleep, * in milliseconds * * RETURN: None * - * DESCRIPTION: Suspend running thread for specified amount of time. + * DESCRIPTION: Sleep the running thread for specified amount of time. * ******************************************************************************/ -acpi_status acpi_ex_system_do_suspend(u64 how_long) +acpi_status acpi_ex_system_do_sleep(u64 how_long) { ACPI_FUNCTION_ENTRY(); diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index ec7fc227b33f..5d1273b660ae 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -299,7 +299,7 @@ struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) ACPI_FUNCTION_ENTRY(); if (register_id > ACPI_BITREG_MAX) { - ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: %X", + ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: 0x%X", register_id)); return (NULL); } @@ -413,7 +413,7 @@ acpi_hw_register_read(u32 register_id, u32 * return_value) break; default: - ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id)); + ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id)); status = AE_BAD_PARAMETER; break; } @@ -549,7 +549,7 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) break; default: - ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id)); + ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id)); status = AE_BAD_PARAMETER; break; } diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index 5e6d4dbb8024..36eb803dd9d0 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -245,7 +245,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) || (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) { - ACPI_ERROR((AE_INFO, "Sleep values out of range: A=%X B=%X", + ACPI_ERROR((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X", acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b)); return_ACPI_STATUS(AE_AML_OPERAND_VALUE); } diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c index e26c17d4b716..c10d587c1641 100644 --- a/drivers/acpi/acpica/hwvalid.c +++ b/drivers/acpi/acpica/hwvalid.c @@ -150,7 +150,7 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width) if (last_address > ACPI_UINT16_MAX) { ACPI_ERROR((AE_INFO, - "Illegal I/O port address/length above 64K: 0x%p/%X", + "Illegal I/O port address/length above 64K: %p/0x%X", ACPI_CAST_PTR(void, address), byte_width)); return_ACPI_STATUS(AE_LIMIT); } diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index aa2b80132d0a..3a2814676ac3 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -222,7 +222,7 @@ acpi_status acpi_ns_root_initialize(void) default: ACPI_ERROR((AE_INFO, - "Unsupported initial type value %X", + "Unsupported initial type value 0x%X", init_val->type)); acpi_ut_remove_reference(obj_desc); obj_desc = NULL; diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index 0689d36638d9..2110cc2360f0 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -205,8 +205,8 @@ acpi_ns_dump_one_object(acpi_handle obj_handle, /* Check the node type and name */ if (type > ACPI_TYPE_LOCAL_MAX) { - ACPI_WARNING((AE_INFO, "Invalid ACPI Object Type %08X", - type)); + ACPI_WARNING((AE_INFO, + "Invalid ACPI Object Type 0x%08X", type)); } if (!acpi_ut_valid_acpi_name(this_node->name.integer)) { diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index 959372451635..7dea0031605c 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -107,7 +107,7 @@ acpi_ns_build_external_path(struct acpi_namespace_node *node, if (index != 0) { ACPI_ERROR((AE_INFO, - "Could not construct external pathname; index=%X, size=%X, Path=%s", + "Could not construct external pathname; index=%u, size=%u, Path=%s", (u32) index, (u32) size, &name_buffer[size])); return (AE_BAD_PARAMETER); diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c index 08f8b3f5ccaa..a8e42b5e9463 100644 --- a/drivers/acpi/acpica/nssearch.c +++ b/drivers/acpi/acpica/nssearch.c @@ -311,7 +311,7 @@ acpi_ns_search_and_enter(u32 target_name, if (!node || !target_name || !return_node) { ACPI_ERROR((AE_INFO, - "Null parameter: Node %p Name %X ReturnNode %p", + "Null parameter: Node %p Name 0x%X ReturnNode %p", node, target_name, return_node)); return_ACPI_STATUS(AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index 24d05a87a2a3..bab559712da1 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -276,7 +276,7 @@ u32 acpi_ns_local(acpi_object_type type) /* Type code out of range */ - ACPI_WARNING((AE_INFO, "Invalid Object Type %X", type)); + ACPI_WARNING((AE_INFO, "Invalid Object Type 0x%X", type)); return_UINT32(ACPI_NS_NORMAL); } @@ -764,7 +764,7 @@ u32 acpi_ns_opens_scope(acpi_object_type type) /* type code out of range */ - ACPI_WARNING((AE_INFO, "Invalid Object Type %X", type)); + ACPI_WARNING((AE_INFO, "Invalid Object Type 0x%X", type)); return_UINT32(ACPI_NS_NORMAL); } diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index 00493e108a01..7df1a4c95274 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -460,7 +460,7 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, default: - ACPI_ERROR((AE_INFO, "Invalid ArgType %X", arg_type)); + ACPI_ERROR((AE_INFO, "Invalid ArgType 0x%X", arg_type)); return_VOID; } @@ -742,7 +742,7 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state, default: - ACPI_ERROR((AE_INFO, "Invalid ArgType: %X", arg_type)); + ACPI_ERROR((AE_INFO, "Invalid ArgType: 0x%X", arg_type)); status = AE_AML_OPERAND_TYPE; break; } diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index 59aabaeab1d3..2f2e7760938c 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -136,7 +136,7 @@ static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state) /* The opcode is unrecognized. Just skip unknown opcodes */ ACPI_ERROR((AE_INFO, - "Found unknown opcode %X at AML address %p offset %X, ignoring", + "Found unknown opcode 0x%X at AML address %p offset 0x%X, ignoring", walk_state->opcode, walk_state->parser_state.aml, walk_state->aml_offset)); @@ -1021,7 +1021,6 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) if (status == AE_AML_NO_RETURN_VALUE) { ACPI_EXCEPTION((AE_INFO, status, "Invoked method did not return a value")); - } ACPI_EXCEPTION((AE_INFO, status, diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index 6064dd4e94c2..c42f067cff9d 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -46,6 +46,7 @@ #include "acparser.h" #include "acdispat.h" #include "acinterp.h" +#include "actables.h" #include "amlcode.h" #define _COMPONENT ACPI_PARSER @@ -220,6 +221,10 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) ACPI_FUNCTION_TRACE(ps_execute_method); + /* Quick validation of DSDT header */ + + acpi_tb_check_dsdt_header(); + /* Validate the Info and method Node */ if (!info || !info->resolved_node) { diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c index f2ee3b548609..c80a2eea3a01 100644 --- a/drivers/acpi/acpica/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -212,7 +212,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, if ((*top_object_list)->common.type != ACPI_TYPE_PACKAGE) { ACPI_ERROR((AE_INFO, - "(PRT[%X]) Need sub-package, found %s", + "(PRT[%u]) Need sub-package, found %s", index, acpi_ut_get_object_type_name (*top_object_list))); @@ -223,7 +223,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, if ((*top_object_list)->package.count != 4) { ACPI_ERROR((AE_INFO, - "(PRT[%X]) Need package of length 4, found length %d", + "(PRT[%u]) Need package of length 4, found length %u", index, (*top_object_list)->package.count)); return_ACPI_STATUS(AE_AML_PACKAGE_LIMIT); } @@ -240,7 +240,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, obj_desc = sub_object_list[0]; if (obj_desc->common.type != ACPI_TYPE_INTEGER) { ACPI_ERROR((AE_INFO, - "(PRT[%X].Address) Need Integer, found %s", + "(PRT[%u].Address) Need Integer, found %s", index, acpi_ut_get_object_type_name(obj_desc))); return_ACPI_STATUS(AE_BAD_DATA); @@ -253,7 +253,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, obj_desc = sub_object_list[1]; if (obj_desc->common.type != ACPI_TYPE_INTEGER) { ACPI_ERROR((AE_INFO, - "(PRT[%X].Pin) Need Integer, found %s", + "(PRT[%u].Pin) Need Integer, found %s", index, acpi_ut_get_object_type_name(obj_desc))); return_ACPI_STATUS(AE_BAD_DATA); @@ -289,7 +289,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, if (obj_desc->reference.class != ACPI_REFCLASS_NAME) { ACPI_ERROR((AE_INFO, - "(PRT[%X].Source) Need name, found Reference Class %X", + "(PRT[%u].Source) Need name, found Reference Class 0x%X", index, obj_desc->reference.class)); return_ACPI_STATUS(AE_BAD_DATA); @@ -340,7 +340,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, default: ACPI_ERROR((AE_INFO, - "(PRT[%X].Source) Need Ref/String/Integer, found %s", + "(PRT[%u].Source) Need Ref/String/Integer, found %s", index, acpi_ut_get_object_type_name (obj_desc))); @@ -358,7 +358,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, obj_desc = sub_object_list[3]; if (obj_desc->common.type != ACPI_TYPE_INTEGER) { ACPI_ERROR((AE_INFO, - "(PRT[%X].SourceIndex) Need Integer, found %s", + "(PRT[%u].SourceIndex) Need Integer, found %s", index, acpi_ut_get_object_type_name(obj_desc))); return_ACPI_STATUS(AE_BAD_DATA); diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c index fd057c72d252..7335f22aac20 100644 --- a/drivers/acpi/acpica/rslist.c +++ b/drivers/acpi/acpica/rslist.c @@ -94,7 +94,7 @@ acpi_rs_convert_aml_to_resources(u8 * aml, [resource_index]); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Could not convert AML resource (Type %X)", + "Could not convert AML resource (Type 0x%X)", *aml)); return_ACPI_STATUS(status); } @@ -147,7 +147,7 @@ acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, if (resource->type > ACPI_RESOURCE_TYPE_MAX) { ACPI_ERROR((AE_INFO, - "Invalid descriptor type (%X) in resource list", + "Invalid descriptor type (0x%X) in resource list", resource->type)); return_ACPI_STATUS(AE_BAD_DATA); } @@ -161,7 +161,7 @@ acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, [resource->type]); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, - "Could not convert resource (type %X) to AML", + "Could not convert resource (type 0x%X) to AML", resource->type)); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c index 07de352fa443..f8cd9e87d987 100644 --- a/drivers/acpi/acpica/rsmisc.c +++ b/drivers/acpi/acpica/rsmisc.c @@ -88,7 +88,7 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, /* Each internal resource struct is expected to be 32-bit aligned */ ACPI_WARNING((AE_INFO, - "Misaligned resource pointer (get): %p Type %2.2X Len %X", + "Misaligned resource pointer (get): %p Type 0x%2.2X Length %u", resource, resource->type, resource->length)); } @@ -541,7 +541,7 @@ if (((aml->irq.flags & 0x09) == 0x00) || ((aml->irq.flags & 0x09) == 0x09)) { * "IRQ Format"), so 0x00 and 0x09 are illegal. */ ACPI_ERROR((AE_INFO, - "Invalid interrupt polarity/trigger in resource list, %X", + "Invalid interrupt polarity/trigger in resource list, 0x%X", aml->irq.flags)); return_ACPI_STATUS(AE_BAD_DATA); } diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index f43fbe0fc3fc..1728cb9bf600 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -283,7 +283,7 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) if (length > sizeof(struct acpi_table_fadt)) { ACPI_WARNING((AE_INFO, "FADT (revision %u) is longer than ACPI 2.0 version, " - "truncating length 0x%X to 0x%X", + "truncating length %u to %u", table->revision, length, (u32)sizeof(struct acpi_table_fadt))); } @@ -422,7 +422,7 @@ static void acpi_tb_convert_fadt(void) if (address64->address && address32 && (address64->address != (u64) address32)) { ACPI_ERROR((AE_INFO, - "32/64X address mismatch in %s: %8.8X/%8.8X%8.8X, using 32", + "32/64X address mismatch in %s: 0x%8.8X/0x%8.8X%8.8X, using 32", fadt_info_table[i].name, address32, ACPI_FORMAT_UINT64(address64->address))); } @@ -481,7 +481,7 @@ static void acpi_tb_validate_fadt(void) (acpi_gbl_FADT.Xfacs != (u64) acpi_gbl_FADT.facs)) { ACPI_WARNING((AE_INFO, "32/64X FACS address mismatch in FADT - " - "%8.8X/%8.8X%8.8X, using 32", + "0x%8.8X/0x%8.8X%8.8X, using 32", acpi_gbl_FADT.facs, ACPI_FORMAT_UINT64(acpi_gbl_FADT.Xfacs))); @@ -492,7 +492,7 @@ static void acpi_tb_validate_fadt(void) (acpi_gbl_FADT.Xdsdt != (u64) acpi_gbl_FADT.dsdt)) { ACPI_WARNING((AE_INFO, "32/64X DSDT address mismatch in FADT - " - "%8.8X/%8.8X%8.8X, using 32", + "0x%8.8X/0x%8.8X%8.8X, using 32", acpi_gbl_FADT.dsdt, ACPI_FORMAT_UINT64(acpi_gbl_FADT.Xdsdt))); @@ -521,7 +521,7 @@ static void acpi_tb_validate_fadt(void) if (address64->address && (address64->bit_width != ACPI_MUL_8(length))) { ACPI_WARNING((AE_INFO, - "32/64X length mismatch in %s: %d/%d", + "32/64X length mismatch in %s: %u/%u", name, ACPI_MUL_8(length), address64->bit_width)); } @@ -534,7 +534,7 @@ static void acpi_tb_validate_fadt(void) if (!address64->address || !length) { ACPI_ERROR((AE_INFO, "Required field %s has zero address and/or length:" - " %8.8X%8.8X/%X", + " 0x%8.8X%8.8X/0x%X", name, ACPI_FORMAT_UINT64(address64-> address), @@ -550,7 +550,7 @@ static void acpi_tb_validate_fadt(void) (!address64->address && length)) { ACPI_WARNING((AE_INFO, "Optional field %s has zero address or length: " - "%8.8X%8.8X/%X", + "0x%8.8X%8.8X/0x%X", name, ACPI_FORMAT_UINT64(address64-> address), @@ -600,7 +600,7 @@ static void acpi_tb_setup_fadt_registers(void) (fadt_info_table[i].default_length != target64->bit_width)) { ACPI_WARNING((AE_INFO, - "Invalid length for %s: %d, using default %d", + "Invalid length for %s: %u, using default %u", fadt_info_table[i].name, target64->bit_width, fadt_info_table[i]. diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c index e252180ce61c..989d5c867864 100644 --- a/drivers/acpi/acpica/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -83,7 +83,7 @@ acpi_tb_find_table(char *signature, /* Search for the table */ - for (i = 0; i < acpi_gbl_root_table_list.count; ++i) { + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { if (ACPI_MEMCMP(&(acpi_gbl_root_table_list.tables[i].signature), header.signature, ACPI_NAME_SIZE)) { diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index 7ec02b0f69e0..83d7af8d0905 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -137,7 +137,7 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index) /* Check if table is already registered */ - for (i = 0; i < acpi_gbl_root_table_list.count; ++i) { + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { if (!acpi_gbl_root_table_list.tables[i].pointer) { status = acpi_tb_verify_table(&acpi_gbl_root_table_list. @@ -273,7 +273,7 @@ acpi_status acpi_tb_resize_root_table_list(void) /* Increase the Table Array size */ tables = ACPI_ALLOCATE_ZEROED(((acpi_size) acpi_gbl_root_table_list. - size + + max_table_count + ACPI_ROOT_TABLE_SIZE_INCREMENT) * sizeof(struct acpi_table_desc)); if (!tables) { @@ -286,8 +286,8 @@ acpi_status acpi_tb_resize_root_table_list(void) if (acpi_gbl_root_table_list.tables) { ACPI_MEMCPY(tables, acpi_gbl_root_table_list.tables, - (acpi_size) acpi_gbl_root_table_list.size * - sizeof(struct acpi_table_desc)); + (acpi_size) acpi_gbl_root_table_list. + max_table_count * sizeof(struct acpi_table_desc)); if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) { ACPI_FREE(acpi_gbl_root_table_list.tables); @@ -295,8 +295,9 @@ acpi_status acpi_tb_resize_root_table_list(void) } acpi_gbl_root_table_list.tables = tables; - acpi_gbl_root_table_list.size += ACPI_ROOT_TABLE_SIZE_INCREMENT; - acpi_gbl_root_table_list.flags |= (u8) ACPI_ROOT_ORIGIN_ALLOCATED; + acpi_gbl_root_table_list.max_table_count += + ACPI_ROOT_TABLE_SIZE_INCREMENT; + acpi_gbl_root_table_list.flags |= (u8)ACPI_ROOT_ORIGIN_ALLOCATED; return_ACPI_STATUS(AE_OK); } @@ -321,38 +322,36 @@ acpi_tb_store_table(acpi_physical_address address, struct acpi_table_header *table, u32 length, u8 flags, u32 *table_index) { - acpi_status status = AE_OK; + acpi_status status; + struct acpi_table_desc *new_table; /* Ensure that there is room for the table in the Root Table List */ - if (acpi_gbl_root_table_list.count >= acpi_gbl_root_table_list.size) { + if (acpi_gbl_root_table_list.current_table_count >= + acpi_gbl_root_table_list.max_table_count) { status = acpi_tb_resize_root_table_list(); if (ACPI_FAILURE(status)) { return (status); } } + new_table = + &acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list. + current_table_count]; + /* Initialize added table */ - acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list.count]. - address = address; - acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list.count]. - pointer = table; - acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list.count].length = - length; - acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list.count]. - owner_id = 0; - acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list.count].flags = - flags; - - ACPI_MOVE_32_TO_32(& - (acpi_gbl_root_table_list. - tables[acpi_gbl_root_table_list.count].signature), - table->signature); - - *table_index = acpi_gbl_root_table_list.count; - acpi_gbl_root_table_list.count++; - return (status); + new_table->address = address; + new_table->pointer = table; + new_table->length = length; + new_table->owner_id = 0; + new_table->flags = flags; + + ACPI_MOVE_32_TO_32(&new_table->signature, table->signature); + + *table_index = acpi_gbl_root_table_list.current_table_count; + acpi_gbl_root_table_list.current_table_count++; + return (AE_OK); } /******************************************************************************* @@ -408,7 +407,7 @@ void acpi_tb_terminate(void) /* Delete the individual tables */ - for (i = 0; i < acpi_gbl_root_table_list.count; ++i) { + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) { acpi_tb_delete_table(&acpi_gbl_root_table_list.tables[i]); } @@ -422,7 +421,7 @@ void acpi_tb_terminate(void) acpi_gbl_root_table_list.tables = NULL; acpi_gbl_root_table_list.flags = 0; - acpi_gbl_root_table_list.count = 0; + acpi_gbl_root_table_list.current_table_count = 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ACPI Tables freed\n")); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); @@ -452,7 +451,7 @@ acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index) return_ACPI_STATUS(status); } - if (table_index >= acpi_gbl_root_table_list.count) { + if (table_index >= acpi_gbl_root_table_list.current_table_count) { /* The table index does not exist */ @@ -505,7 +504,7 @@ acpi_status acpi_tb_allocate_owner_id(u32 table_index) ACPI_FUNCTION_TRACE(tb_allocate_owner_id); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - if (table_index < acpi_gbl_root_table_list.count) { + if (table_index < acpi_gbl_root_table_list.current_table_count) { status = acpi_ut_allocate_owner_id (&(acpi_gbl_root_table_list.tables[table_index].owner_id)); } @@ -533,7 +532,7 @@ acpi_status acpi_tb_release_owner_id(u32 table_index) ACPI_FUNCTION_TRACE(tb_release_owner_id); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - if (table_index < acpi_gbl_root_table_list.count) { + if (table_index < acpi_gbl_root_table_list.current_table_count) { acpi_ut_release_owner_id(& (acpi_gbl_root_table_list. tables[table_index].owner_id)); @@ -564,7 +563,7 @@ acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id *owner_id) ACPI_FUNCTION_TRACE(tb_get_owner_id); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - if (table_index < acpi_gbl_root_table_list.count) { + if (table_index < acpi_gbl_root_table_list.current_table_count) { *owner_id = acpi_gbl_root_table_list.tables[table_index].owner_id; status = AE_OK; @@ -589,7 +588,7 @@ u8 acpi_tb_is_table_loaded(u32 table_index) u8 is_loaded = FALSE; (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - if (table_index < acpi_gbl_root_table_list.count) { + if (table_index < acpi_gbl_root_table_list.current_table_count) { is_loaded = (u8) (acpi_gbl_root_table_list.tables[table_index].flags & ACPI_TABLE_IS_LOADED); @@ -616,7 +615,7 @@ void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded) { (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - if (table_index < acpi_gbl_root_table_list.count) { + if (table_index < acpi_gbl_root_table_list.current_table_count) { if (is_loaded) { acpi_gbl_root_table_list.tables[table_index].flags |= ACPI_TABLE_IS_LOADED; diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index 02723a9fb10c..34f9c2bc5e1f 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -158,7 +158,7 @@ acpi_status acpi_tb_initialize_facs(void) u8 acpi_tb_tables_loaded(void) { - if (acpi_gbl_root_table_list.count >= 3) { + if (acpi_gbl_root_table_list.current_table_count >= 3) { return (TRUE); } @@ -309,7 +309,7 @@ acpi_status acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length) if (checksum) { ACPI_WARNING((AE_INFO, - "Incorrect checksum in table [%4.4s] - %2.2X, should be %2.2X", + "Incorrect checksum in table [%4.4s] - 0x%2.2X, should be 0x%2.2X", table->signature, table->checksum, (u8) (table->checksum - checksum))); @@ -349,6 +349,84 @@ u8 acpi_tb_checksum(u8 *buffer, u32 length) /******************************************************************************* * + * FUNCTION: acpi_tb_check_dsdt_header + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Quick compare to check validity of the DSDT. This will detect + * if the DSDT has been replaced from outside the OS and/or if + * the DSDT header has been corrupted. + * + ******************************************************************************/ + +void acpi_tb_check_dsdt_header(void) +{ + + /* Compare original length and checksum to current values */ + + if (acpi_gbl_original_dsdt_header.length != acpi_gbl_DSDT->length || + acpi_gbl_original_dsdt_header.checksum != acpi_gbl_DSDT->checksum) { + ACPI_ERROR((AE_INFO, + "The DSDT has been corrupted or replaced - old, new headers below")); + acpi_tb_print_table_header(0, &acpi_gbl_original_dsdt_header); + acpi_tb_print_table_header(0, acpi_gbl_DSDT); + + ACPI_ERROR((AE_INFO, + "Please send DMI info to linux-acpi@vger.kernel.org\n" + "If system does not work as expected, please boot with acpi=copy_dsdt")); + + /* Disable further error messages */ + + acpi_gbl_original_dsdt_header.length = acpi_gbl_DSDT->length; + acpi_gbl_original_dsdt_header.checksum = + acpi_gbl_DSDT->checksum; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_copy_dsdt + * + * PARAMETERS: table_desc - Installed table to copy + * + * RETURN: None + * + * DESCRIPTION: Implements a subsystem option to copy the DSDT to local memory. + * Some very bad BIOSs are known to either corrupt the DSDT or + * install a new, bad DSDT. This copy works around the problem. + * + ******************************************************************************/ + +struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index) +{ + struct acpi_table_header *new_table; + struct acpi_table_desc *table_desc; + + table_desc = &acpi_gbl_root_table_list.tables[table_index]; + + new_table = ACPI_ALLOCATE(table_desc->length); + if (!new_table) { + ACPI_ERROR((AE_INFO, "Could not copy DSDT of length 0x%X", + table_desc->length)); + return (NULL); + } + + ACPI_MEMCPY(new_table, table_desc->pointer, table_desc->length); + acpi_tb_delete_table(table_desc); + table_desc->pointer = new_table; + table_desc->flags = ACPI_TABLE_ORIGIN_ALLOCATED; + + ACPI_INFO((AE_INFO, + "Forced DSDT copy: length 0x%05X copied locally, original unmapped", + new_table->length)); + + return (new_table); +} + +/******************************************************************************* + * * FUNCTION: acpi_tb_install_table * * PARAMETERS: Address - Physical address of DSDT or FACS @@ -496,7 +574,7 @@ acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size) /* Will truncate 64-bit address to 32 bits, issue warning */ ACPI_WARNING((AE_INFO, - "64-bit Physical Address in XSDT is too large (%8.8X%8.8X)," + "64-bit Physical Address in XSDT is too large (0x%8.8X%8.8X)," " truncating", ACPI_FORMAT_UINT64(address64))); } @@ -629,14 +707,14 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address) */ table_entry = ACPI_CAST_PTR(u8, table) + sizeof(struct acpi_table_header); - acpi_gbl_root_table_list.count = 2; + acpi_gbl_root_table_list.current_table_count = 2; /* * Initialize the root table array from the RSDT/XSDT */ for (i = 0; i < table_count; i++) { - if (acpi_gbl_root_table_list.count >= - acpi_gbl_root_table_list.size) { + if (acpi_gbl_root_table_list.current_table_count >= + acpi_gbl_root_table_list.max_table_count) { /* There is no more room in the root table array, attempt resize */ @@ -646,19 +724,20 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address) "Truncating %u table entries!", (unsigned) (table_count - (acpi_gbl_root_table_list. - count - 2)))); + current_table_count - + 2)))); break; } } /* Get the table physical address (32-bit for RSDT, 64-bit for XSDT) */ - acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list.count]. - address = + acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list. + current_table_count].address = acpi_tb_get_root_table_entry(table_entry, table_entry_size); table_entry += table_entry_size; - acpi_gbl_root_table_list.count++; + acpi_gbl_root_table_list.current_table_count++; } /* @@ -671,7 +750,7 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address) * Complete the initialization of the root table array by examining * the header of each table */ - for (i = 2; i < acpi_gbl_root_table_list.count; i++) { + for (i = 2; i < acpi_gbl_root_table_list.current_table_count; i++) { acpi_tb_install_table(acpi_gbl_root_table_list.tables[i]. address, NULL, i); diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index 5217a6159a31..4a8b9e6ea57a 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -72,7 +72,7 @@ static int no_auto_ssdt; acpi_status acpi_allocate_root_table(u32 initial_table_count) { - acpi_gbl_root_table_list.size = initial_table_count; + acpi_gbl_root_table_list.max_table_count = initial_table_count; acpi_gbl_root_table_list.flags = ACPI_ROOT_ALLOW_RESIZE; return (acpi_tb_resize_root_table_list()); @@ -130,7 +130,7 @@ acpi_initialize_tables(struct acpi_table_desc * initial_table_array, sizeof(struct acpi_table_desc)); acpi_gbl_root_table_list.tables = initial_table_array; - acpi_gbl_root_table_list.size = initial_table_count; + acpi_gbl_root_table_list.max_table_count = initial_table_count; acpi_gbl_root_table_list.flags = ACPI_ROOT_ORIGIN_UNKNOWN; if (allow_resize) { acpi_gbl_root_table_list.flags |= @@ -172,6 +172,7 @@ acpi_status acpi_reallocate_root_table(void) { struct acpi_table_desc *tables; acpi_size new_size; + acpi_size current_size; ACPI_FUNCTION_TRACE(acpi_reallocate_root_table); @@ -183,10 +184,17 @@ acpi_status acpi_reallocate_root_table(void) return_ACPI_STATUS(AE_SUPPORT); } - new_size = ((acpi_size) acpi_gbl_root_table_list.count + - ACPI_ROOT_TABLE_SIZE_INCREMENT) * + /* + * Get the current size of the root table and add the default + * increment to create the new table size. + */ + current_size = (acpi_size) + acpi_gbl_root_table_list.current_table_count * sizeof(struct acpi_table_desc); + new_size = current_size + + (ACPI_ROOT_TABLE_SIZE_INCREMENT * sizeof(struct acpi_table_desc)); + /* Create new array and copy the old array */ tables = ACPI_ALLOCATE_ZEROED(new_size); @@ -194,10 +202,17 @@ acpi_status acpi_reallocate_root_table(void) return_ACPI_STATUS(AE_NO_MEMORY); } - ACPI_MEMCPY(tables, acpi_gbl_root_table_list.tables, new_size); + ACPI_MEMCPY(tables, acpi_gbl_root_table_list.tables, current_size); - acpi_gbl_root_table_list.size = acpi_gbl_root_table_list.count; + /* + * Update the root table descriptor. The new size will be the current + * number of tables plus the increment, independent of the reserved + * size of the original table list. + */ acpi_gbl_root_table_list.tables = tables; + acpi_gbl_root_table_list.max_table_count = + acpi_gbl_root_table_list.current_table_count + + ACPI_ROOT_TABLE_SIZE_INCREMENT; acpi_gbl_root_table_list.flags = ACPI_ROOT_ORIGIN_ALLOCATED | ACPI_ROOT_ALLOW_RESIZE; @@ -278,7 +293,8 @@ acpi_get_table_header(char *signature, /* Walk the root table list */ - for (i = 0, j = 0; i < acpi_gbl_root_table_list.count; i++) { + for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count; + i++) { if (!ACPI_COMPARE_NAME (&(acpi_gbl_root_table_list.tables[i].signature), signature)) { @@ -341,7 +357,7 @@ acpi_status acpi_unload_table_id(acpi_owner_id id) ACPI_FUNCTION_TRACE(acpi_unload_table_id); /* Find table in the global table list */ - for (i = 0; i < acpi_gbl_root_table_list.count; ++i) { + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { if (id != acpi_gbl_root_table_list.tables[i].owner_id) { continue; } @@ -391,7 +407,8 @@ acpi_get_table_with_size(char *signature, /* Walk the root table list */ - for (i = 0, j = 0; i < acpi_gbl_root_table_list.count; i++) { + for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count; + i++) { if (!ACPI_COMPARE_NAME (&(acpi_gbl_root_table_list.tables[i].signature), signature)) { @@ -459,7 +476,7 @@ acpi_get_table_by_index(u32 table_index, struct acpi_table_header **table) /* Validate index */ - if (table_index >= acpi_gbl_root_table_list.count) { + if (table_index >= acpi_gbl_root_table_list.current_table_count) { (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); return_ACPI_STATUS(AE_BAD_PARAMETER); } @@ -500,16 +517,17 @@ static acpi_status acpi_tb_load_namespace(void) { acpi_status status; u32 i; + struct acpi_table_header *new_dsdt; ACPI_FUNCTION_TRACE(tb_load_namespace); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); /* - * Load the namespace. The DSDT is required, but any SSDT and PSDT tables - * are optional. + * Load the namespace. The DSDT is required, but any SSDT and + * PSDT tables are optional. Verify the DSDT. */ - if (!acpi_gbl_root_table_list.count || + if (!acpi_gbl_root_table_list.current_table_count || !ACPI_COMPARE_NAME(& (acpi_gbl_root_table_list. tables[ACPI_TABLE_INDEX_DSDT].signature), @@ -522,17 +540,35 @@ static acpi_status acpi_tb_load_namespace(void) goto unlock_and_exit; } - /* A valid DSDT is required */ - - status = - acpi_tb_verify_table(&acpi_gbl_root_table_list. - tables[ACPI_TABLE_INDEX_DSDT]); - if (ACPI_FAILURE(status)) { + /* + * Save the DSDT pointer for simple access. This is the mapped memory + * address. We must take care here because the address of the .Tables + * array can change dynamically as tables are loaded at run-time. Note: + * .Pointer field is not validated until after call to acpi_tb_verify_table. + */ + acpi_gbl_DSDT = + acpi_gbl_root_table_list.tables[ACPI_TABLE_INDEX_DSDT].pointer; - status = AE_NO_ACPI_TABLES; - goto unlock_and_exit; + /* + * Optionally copy the entire DSDT to local memory (instead of simply + * mapping it.) There are some BIOSs that corrupt or replace the original + * DSDT, creating the need for this option. Default is FALSE, do not copy + * the DSDT. + */ + if (acpi_gbl_copy_dsdt_locally) { + new_dsdt = acpi_tb_copy_dsdt(ACPI_TABLE_INDEX_DSDT); + if (new_dsdt) { + acpi_gbl_DSDT = new_dsdt; + } } + /* + * Save the original DSDT header for detection of table corruption + * and/or replacement of the DSDT from outside the OS. + */ + ACPI_MEMCPY(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT, + sizeof(struct acpi_table_header)); + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); /* Load and parse tables */ @@ -545,7 +581,7 @@ static acpi_status acpi_tb_load_namespace(void) /* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - for (i = 0; i < acpi_gbl_root_table_list.count; ++i) { + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { if ((!ACPI_COMPARE_NAME (&(acpi_gbl_root_table_list.tables[i].signature), ACPI_SIG_SSDT) diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index dda6e8c497d3..fd2c07d1d3ac 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -134,7 +134,7 @@ acpi_status acpi_find_root_pointer(acpi_size *table_address) ACPI_EBDA_PTR_LENGTH); if (!table_ptr) { ACPI_ERROR((AE_INFO, - "Could not map memory at %8.8X for length %X", + "Could not map memory at 0x%8.8X for length %u", ACPI_EBDA_PTR_LOCATION, ACPI_EBDA_PTR_LENGTH)); return_ACPI_STATUS(AE_NO_MEMORY); @@ -159,7 +159,7 @@ acpi_status acpi_find_root_pointer(acpi_size *table_address) ACPI_EBDA_WINDOW_SIZE); if (!table_ptr) { ACPI_ERROR((AE_INFO, - "Could not map memory at %8.8X for length %X", + "Could not map memory at 0x%8.8X for length %u", physical_address, ACPI_EBDA_WINDOW_SIZE)); return_ACPI_STATUS(AE_NO_MEMORY); @@ -191,7 +191,7 @@ acpi_status acpi_find_root_pointer(acpi_size *table_address) if (!table_ptr) { ACPI_ERROR((AE_INFO, - "Could not map memory at %8.8X for length %X", + "Could not map memory at 0x%8.8X for length %u", ACPI_HI_RSDP_WINDOW_BASE, ACPI_HI_RSDP_WINDOW_SIZE)); diff --git a/drivers/acpi/acpica/utalloc.c b/drivers/acpi/acpica/utalloc.c index 3d706b8fd449..8f0896281567 100644 --- a/drivers/acpi/acpica/utalloc.c +++ b/drivers/acpi/acpica/utalloc.c @@ -340,7 +340,7 @@ void *acpi_ut_allocate(acpi_size size, /* Report allocation error */ ACPI_WARNING((module, line, - "Could not allocate size %X", (u32) size)); + "Could not allocate size %u", (u32) size)); return_PTR(NULL); } diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index 97ec3621e71d..6fef83f04bcd 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -677,16 +677,24 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, u16 reference_count; union acpi_operand_object *next_object; acpi_status status; + acpi_size copy_size; /* Save fields from destination that we don't want to overwrite */ reference_count = dest_desc->common.reference_count; next_object = dest_desc->common.next_object; - /* Copy the entire source object over the destination object */ + /* + * Copy the entire source object over the destination object. + * Note: Source can be either an operand object or namespace node. + */ + copy_size = sizeof(union acpi_operand_object); + if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_NAMED) { + copy_size = sizeof(struct acpi_namespace_node); + } - ACPI_MEMCPY((char *)dest_desc, (char *)source_desc, - sizeof(union acpi_operand_object)); + ACPI_MEMCPY(ACPI_CAST_PTR(char, dest_desc), + ACPI_CAST_PTR(char, source_desc), copy_size); /* Restore the saved fields */ diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index 16b51c69606a..ed794cd033ea 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -434,7 +434,7 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) default: - ACPI_ERROR((AE_INFO, "Unknown action (%X)", action)); + ACPI_ERROR((AE_INFO, "Unknown action (0x%X)", action)); break; } @@ -444,8 +444,8 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) */ if (count > ACPI_MAX_REFERENCE_COUNT) { ACPI_WARNING((AE_INFO, - "Large Reference Count (%X) in object %p", count, - object)); + "Large Reference Count (0x%X) in object %p", + count, object)); } } diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index 7f5e734ce7f7..6dfdeb653490 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -307,7 +307,7 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, prefix_node, path, AE_TYPE); ACPI_ERROR((AE_INFO, - "Type returned from %s was incorrect: %s, expected Btypes: %X", + "Type returned from %s was incorrect: %s, expected Btypes: 0x%X", path, acpi_ut_get_object_type_name(info->return_object), expected_return_btypes)); diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index eda3e656c4af..66116750a0f9 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -785,6 +785,7 @@ acpi_status acpi_ut_init_globals(void) /* Miscellaneous variables */ + acpi_gbl_DSDT = NULL; acpi_gbl_cm_single_step = FALSE; acpi_gbl_db_terminate_threads = FALSE; acpi_gbl_shutdown = FALSE; diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index 32982e2ac384..e8d0724ee403 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -205,7 +205,7 @@ acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id) /* Guard against multiple allocations of ID to the same location */ if (*owner_id) { - ACPI_ERROR((AE_INFO, "Owner ID [%2.2X] already exists", + ACPI_ERROR((AE_INFO, "Owner ID [0x%2.2X] already exists", *owner_id)); return_ACPI_STATUS(AE_ALREADY_EXISTS); } @@ -315,7 +315,7 @@ void acpi_ut_release_owner_id(acpi_owner_id * owner_id_ptr) /* Zero is not a valid owner_iD */ if (owner_id == 0) { - ACPI_ERROR((AE_INFO, "Invalid OwnerId: %2.2X", owner_id)); + ACPI_ERROR((AE_INFO, "Invalid OwnerId: 0x%2.2X", owner_id)); return_VOID; } @@ -341,7 +341,7 @@ void acpi_ut_release_owner_id(acpi_owner_id * owner_id_ptr) acpi_gbl_owner_id_mask[index] ^= bit; } else { ACPI_ERROR((AE_INFO, - "Release of non-allocated OwnerId: %2.2X", + "Release of non-allocated OwnerId: 0x%2.2X", owner_id + 1)); } diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 55d014ed6d55..058b3df48271 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -258,7 +258,7 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id; } else { ACPI_EXCEPTION((AE_INFO, status, - "Thread %p could not acquire Mutex [%X]", + "Thread %p could not acquire Mutex [0x%X]", ACPI_CAST_PTR(void, this_thread_id), mutex_id)); } @@ -297,7 +297,7 @@ acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) */ if (acpi_gbl_mutex_info[mutex_id].thread_id == ACPI_MUTEX_NOT_ACQUIRED) { ACPI_ERROR((AE_INFO, - "Mutex [%X] is not acquired, cannot release", + "Mutex [0x%X] is not acquired, cannot release", mutex_id)); return (AE_NOT_ACQUIRED); diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index 3356f0cb0745..fd1fa2749ea5 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -251,7 +251,7 @@ union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size) buffer = ACPI_ALLOCATE_ZEROED(buffer_size); if (!buffer) { - ACPI_ERROR((AE_INFO, "Could not allocate size %X", + ACPI_ERROR((AE_INFO, "Could not allocate size %u", (u32) buffer_size)); acpi_ut_remove_reference(buffer_desc); return_PTR(NULL); @@ -303,7 +303,7 @@ union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size) */ string = ACPI_ALLOCATE_ZEROED(string_size + 1); if (!string) { - ACPI_ERROR((AE_INFO, "Could not allocate size %X", + ACPI_ERROR((AE_INFO, "Could not allocate size %u", (u32) string_size)); acpi_ut_remove_reference(string_desc); return_PTR(NULL); @@ -533,7 +533,7 @@ acpi_ut_get_simple_object_size(union acpi_operand_object *internal_object, */ ACPI_ERROR((AE_INFO, "Cannot convert to external object - " - "unsupported Reference Class [%s] %X in object %p", + "unsupported Reference Class [%s] 0x%X in object %p", acpi_ut_get_reference_name(internal_object), internal_object->reference.class, internal_object)); @@ -545,7 +545,7 @@ acpi_ut_get_simple_object_size(union acpi_operand_object *internal_object, default: ACPI_ERROR((AE_INFO, "Cannot convert to external object - " - "unsupported type [%s] %X in object %p", + "unsupported type [%s] 0x%X in object %p", acpi_ut_get_object_type_name(internal_object), internal_object->common.type, internal_object)); status = AE_TYPE; diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 743576bf1bd7..9042a8579668 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -69,6 +69,44 @@ static struct dmi_system_id __cpuinitdata power_nocheck_dmi_table[] = { }; +#ifdef CONFIG_X86 +static int set_copy_dsdt(const struct dmi_system_id *id) +{ + printk(KERN_NOTICE "%s detected - " + "force copy of DSDT to local memory\n", id->ident); + acpi_gbl_copy_dsdt_locally = 1; + return 0; +} + +static struct dmi_system_id dsdt_dmi_table[] __initdata = { + /* + * Insyde BIOS on some TOSHIBA machines corrupt the DSDT. + * https://bugzilla.kernel.org/show_bug.cgi?id=14679 + */ + { + .callback = set_copy_dsdt, + .ident = "TOSHIBA Satellite A505", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A505"), + }, + }, + { + .callback = set_copy_dsdt, + .ident = "TOSHIBA Satellite L505D", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite L505D"), + }, + }, + {} +}; +#else +static struct dmi_system_id dsdt_dmi_table[] __initdata = { + {} +}; +#endif + /* -------------------------------------------------------------------------- Device Management -------------------------------------------------------------------------- */ @@ -813,6 +851,12 @@ void __init acpi_early_init(void) acpi_gbl_permanent_mmap = 1; + /* + * If the machine falls into the DMI check table, + * DSDT will be copied to memory + */ + dmi_check_system(dsdt_dmi_table); + status = acpi_reallocate_root_table(); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7594f65800cf..4bc1c4178f50 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1406,7 +1406,7 @@ acpi_os_invalidate_address( switch (space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: case ACPI_ADR_SPACE_SYSTEM_MEMORY: - /* Only interference checks against SystemIO and SytemMemory + /* Only interference checks against SystemIO and SystemMemory are needed */ res.start = address; res.end = address + length - 1; @@ -1458,7 +1458,7 @@ acpi_os_validate_address ( switch (space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: case ACPI_ADR_SPACE_SYSTEM_MEMORY: - /* Only interference checks against SystemIO and SytemMemory + /* Only interference checks against SystemIO and SystemMemory are needed */ res = kzalloc(sizeof(struct acpi_res_list), GFP_KERNEL); if (!res) diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index b0a71ecee682..e4804fb05e23 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -401,11 +401,13 @@ int acpi_pci_irq_enable(struct pci_dev *dev) * driver reported one, then use it. Exit in any case. */ if (gsi < 0) { + u32 dev_gsi; dev_warn(&dev->dev, "PCI INT %c: no GSI", pin_name(pin)); /* Interrupt Line values above 0xF are forbidden */ - if (dev->irq > 0 && (dev->irq <= 0xF)) { - printk(" - using IRQ %d\n", dev->irq); - acpi_register_gsi(&dev->dev, dev->irq, + if (dev->irq > 0 && (dev->irq <= 0xF) && + (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) { + printk(" - using ISA IRQ %d\n", dev->irq); + acpi_register_gsi(&dev->dev, dev_gsi, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW); return 0; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index ddc76787b842..f74d3b31e5c9 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -172,7 +172,6 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) return -EINVAL; /* The state of the list is 'on' IFF all resources are 'on'. */ - /* */ for (i = 0; i < list->count; i++) { /* diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 5939e7f7d8e9..c3817e1f32c7 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -698,7 +698,7 @@ static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset) "max_cstate: C%d\n" "maximum allowed latency: %d usec\n", pr->power.state ? pr->power.state - pr->power.states : 0, - max_cstate, pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY)); + max_cstate, pm_qos_request(PM_QOS_CPU_DMA_LATENCY)); seq_puts(seq, "states:\n"); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0338f513a010..7f2e051ed4f1 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -765,7 +765,7 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device) } status = acpi_get_gpe_status(NULL, device->wakeup.gpe_number, - ACPI_NOT_ISR, &event_status); + &event_status); if (status == AE_OK) device->wakeup.flags.run_wake = !!(event_status & ACPI_EVENT_FLAG_HANDLE); diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 4aaf24976138..e35525b39f6b 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -303,8 +303,7 @@ static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) "Invalid GPE 0x%x\n", index)); goto end; } - result = acpi_get_gpe_status(*handle, index, - ACPI_NOT_ISR, status); + result = acpi_get_gpe_status(*handle, index, status); } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) result = acpi_get_event_status(index - num_gpes, status); @@ -395,7 +394,7 @@ static ssize_t counter_set(struct kobject *kobj, result = acpi_set_gpe(handle, index, ACPI_GPE_ENABLE); else if (!strcmp(buf, "clear\n") && (status & ACPI_EVENT_FLAG_SET)) - result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR); + result = acpi_clear_gpe(handle, index); else all_counters[index].count = strtoul(buf, NULL, 0); } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) { diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 01c52c415bdc..e68541f662b9 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -65,6 +65,14 @@ config SATA_AHCI If unsure, say N. +config SATA_AHCI_PLATFORM + tristate "Platform AHCI SATA support" + help + This option enables support for Platform AHCI Serial ATA + controllers. + + If unsure, say N. + config SATA_SIL24 tristate "Silicon Image 3124/3132 SATA support" depends on PCI @@ -73,6 +81,12 @@ config SATA_SIL24 If unsure, say N. +config SATA_INIC162X + tristate "Initio 162x SATA support" + depends on PCI + help + This option enables support for Initio 162x Serial ATA. + config SATA_FSL tristate "Freescale 3.0Gbps SATA support" depends on FSL_SOC @@ -213,12 +227,6 @@ config SATA_VITESSE If unsure, say N. -config SATA_INIC162X - tristate "Initio 162x SATA support" - depends on PCI - help - This option enables support for Initio 162x Serial ATA. - config PATA_ACPI tristate "ACPI firmware driver for PATA" depends on ATA_ACPI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index fc936d4471d6..d0a93c4ad3ec 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_ATA) += libata.o -obj-$(CONFIG_SATA_AHCI) += ahci.o +obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o +obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o obj-$(CONFIG_SATA_SVW) += sata_svw.o obj-$(CONFIG_ATA_PIIX) += ata_piix.o obj-$(CONFIG_SATA_PROMISE) += sata_promise.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 5326af28a410..8ca16f54e1ed 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -46,403 +46,48 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <linux/libata.h> +#include "ahci.h" #define DRV_NAME "ahci" #define DRV_VERSION "3.0" -/* Enclosure Management Control */ -#define EM_CTRL_MSG_TYPE 0x000f0000 - -/* Enclosure Management LED Message Type */ -#define EM_MSG_LED_HBA_PORT 0x0000000f -#define EM_MSG_LED_PMP_SLOT 0x0000ff00 -#define EM_MSG_LED_VALUE 0xffff0000 -#define EM_MSG_LED_VALUE_ACTIVITY 0x00070000 -#define EM_MSG_LED_VALUE_OFF 0xfff80000 -#define EM_MSG_LED_VALUE_ON 0x00010000 - -static int ahci_skip_host_reset; -static int ahci_ignore_sss; - -module_param_named(skip_host_reset, ahci_skip_host_reset, int, 0444); -MODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip)"); - -module_param_named(ignore_sss, ahci_ignore_sss, int, 0444); -MODULE_PARM_DESC(ignore_sss, "Ignore staggered spinup flag (0=don't ignore, 1=ignore)"); - -static int ahci_enable_alpm(struct ata_port *ap, - enum link_pm policy); -static void ahci_disable_alpm(struct ata_port *ap); -static ssize_t ahci_led_show(struct ata_port *ap, char *buf); -static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, - size_t size); -static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, - ssize_t size); - enum { AHCI_PCI_BAR = 5, - AHCI_MAX_PORTS = 32, - AHCI_MAX_SG = 168, /* hardware max is 64K */ - AHCI_DMA_BOUNDARY = 0xffffffff, - AHCI_MAX_CMDS = 32, - AHCI_CMD_SZ = 32, - AHCI_CMD_SLOT_SZ = AHCI_MAX_CMDS * AHCI_CMD_SZ, - AHCI_RX_FIS_SZ = 256, - AHCI_CMD_TBL_CDB = 0x40, - AHCI_CMD_TBL_HDR_SZ = 0x80, - AHCI_CMD_TBL_SZ = AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16), - AHCI_CMD_TBL_AR_SZ = AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS, - AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + - AHCI_RX_FIS_SZ, - AHCI_PORT_PRIV_FBS_DMA_SZ = AHCI_CMD_SLOT_SZ + - AHCI_CMD_TBL_AR_SZ + - (AHCI_RX_FIS_SZ * 16), - AHCI_IRQ_ON_SG = (1 << 31), - AHCI_CMD_ATAPI = (1 << 5), - AHCI_CMD_WRITE = (1 << 6), - AHCI_CMD_PREFETCH = (1 << 7), - AHCI_CMD_RESET = (1 << 8), - AHCI_CMD_CLR_BUSY = (1 << 10), - - RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ - RX_FIS_SDB = 0x58, /* offset of SDB FIS data */ - RX_FIS_UNK = 0x60, /* offset of Unknown FIS data */ - - board_ahci = 0, - board_ahci_vt8251 = 1, - board_ahci_ign_iferr = 2, - board_ahci_sb600 = 3, - board_ahci_mv = 4, - board_ahci_sb700 = 5, /* for SB700 and SB800 */ - board_ahci_mcp65 = 6, - board_ahci_nopmp = 7, - board_ahci_yesncq = 8, - board_ahci_nosntf = 9, - - /* global controller registers */ - HOST_CAP = 0x00, /* host capabilities */ - HOST_CTL = 0x04, /* global host control */ - HOST_IRQ_STAT = 0x08, /* interrupt status */ - HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ - HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ - HOST_EM_LOC = 0x1c, /* Enclosure Management location */ - HOST_EM_CTL = 0x20, /* Enclosure Management Control */ - HOST_CAP2 = 0x24, /* host capabilities, extended */ - - /* HOST_CTL bits */ - HOST_RESET = (1 << 0), /* reset controller; self-clear */ - HOST_IRQ_EN = (1 << 1), /* global IRQ enable */ - HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ - - /* HOST_CAP bits */ - HOST_CAP_SXS = (1 << 5), /* Supports External SATA */ - HOST_CAP_EMS = (1 << 6), /* Enclosure Management support */ - HOST_CAP_CCC = (1 << 7), /* Command Completion Coalescing */ - HOST_CAP_PART = (1 << 13), /* Partial state capable */ - HOST_CAP_SSC = (1 << 14), /* Slumber state capable */ - HOST_CAP_PIO_MULTI = (1 << 15), /* PIO multiple DRQ support */ - HOST_CAP_FBS = (1 << 16), /* FIS-based switching support */ - HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ - HOST_CAP_ONLY = (1 << 18), /* Supports AHCI mode only */ - HOST_CAP_CLO = (1 << 24), /* Command List Override support */ - HOST_CAP_LED = (1 << 25), /* Supports activity LED */ - HOST_CAP_ALPM = (1 << 26), /* Aggressive Link PM support */ - HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ - HOST_CAP_MPS = (1 << 28), /* Mechanical presence switch */ - HOST_CAP_SNTF = (1 << 29), /* SNotification register */ - HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ - HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ - - /* HOST_CAP2 bits */ - HOST_CAP2_BOH = (1 << 0), /* BIOS/OS handoff supported */ - HOST_CAP2_NVMHCI = (1 << 1), /* NVMHCI supported */ - HOST_CAP2_APST = (1 << 2), /* Automatic partial to slumber */ - - /* registers for each SATA port */ - PORT_LST_ADDR = 0x00, /* command list DMA addr */ - PORT_LST_ADDR_HI = 0x04, /* command list DMA addr hi */ - PORT_FIS_ADDR = 0x08, /* FIS rx buf addr */ - PORT_FIS_ADDR_HI = 0x0c, /* FIS rx buf addr hi */ - PORT_IRQ_STAT = 0x10, /* interrupt status */ - PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */ - PORT_CMD = 0x18, /* port command */ - PORT_TFDATA = 0x20, /* taskfile data */ - PORT_SIG = 0x24, /* device TF signature */ - PORT_CMD_ISSUE = 0x38, /* command issue */ - PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */ - PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ - PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ - PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ - PORT_SCR_NTF = 0x3c, /* SATA phy register: SNotification */ - PORT_FBS = 0x40, /* FIS-based Switching */ - - /* PORT_IRQ_{STAT,MASK} bits */ - PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ - PORT_IRQ_TF_ERR = (1 << 30), /* task file error */ - PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */ - PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */ - PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */ - PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */ - PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */ - PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */ - - PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */ - PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */ - PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */ - PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */ - PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */ - PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */ - PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */ - PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */ - PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */ - - PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR | - PORT_IRQ_IF_ERR | - PORT_IRQ_CONNECT | - PORT_IRQ_PHYRDY | - PORT_IRQ_UNK_FIS | - PORT_IRQ_BAD_PMP, - PORT_IRQ_ERROR = PORT_IRQ_FREEZE | - PORT_IRQ_TF_ERR | - PORT_IRQ_HBUS_DATA_ERR, - DEF_PORT_IRQ = PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | - PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | - PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS, - - /* PORT_CMD bits */ - PORT_CMD_ASP = (1 << 27), /* Aggressive Slumber/Partial */ - PORT_CMD_ALPE = (1 << 26), /* Aggressive Link PM enable */ - PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */ - PORT_CMD_FBSCP = (1 << 22), /* FBS Capable Port */ - PORT_CMD_PMP = (1 << 17), /* PMP attached */ - PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ - PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */ - PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */ - PORT_CMD_CLO = (1 << 3), /* Command list override */ - PORT_CMD_POWER_ON = (1 << 2), /* Power up device */ - PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ - PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ - - PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */ - PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ - PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ - PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ - - PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */ - PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */ - PORT_FBS_DEV_OFFSET = 8, /* FBS device to issue offset */ - PORT_FBS_DEV_MASK = (0xf << PORT_FBS_DEV_OFFSET), /* FBS.DEV */ - PORT_FBS_SDE = (1 << 2), /* FBS single device error */ - PORT_FBS_DEC = (1 << 1), /* FBS device error clear */ - PORT_FBS_EN = (1 << 0), /* Enable FBS */ - - /* hpriv->flags bits */ - AHCI_HFLAG_NO_NCQ = (1 << 0), - AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */ - AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */ - AHCI_HFLAG_32BIT_ONLY = (1 << 3), /* force 32bit */ - AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */ - AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */ - AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */ - AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */ - AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */ - AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */ - AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */ - AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as - link offline */ - AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */ - - /* ap->flags bits */ - - AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_ACPI_SATA | ATA_FLAG_AN | - ATA_FLAG_IPM, - - ICH_MAP = 0x90, /* ICH MAP register */ - - /* em constants */ - EM_MAX_SLOTS = 8, - EM_MAX_RETRY = 5, - - /* em_ctl bits */ - EM_CTL_RST = (1 << 9), /* Reset */ - EM_CTL_TM = (1 << 8), /* Transmit Message */ - EM_CTL_ALHD = (1 << 26), /* Activity LED */ -}; - -struct ahci_cmd_hdr { - __le32 opts; - __le32 status; - __le32 tbl_addr; - __le32 tbl_addr_hi; - __le32 reserved[4]; -}; - -struct ahci_sg { - __le32 addr; - __le32 addr_hi; - __le32 reserved; - __le32 flags_size; -}; - -struct ahci_em_priv { - enum sw_activity blink_policy; - struct timer_list timer; - unsigned long saved_activity; - unsigned long activity; - unsigned long led_state; -}; - -struct ahci_host_priv { - unsigned int flags; /* AHCI_HFLAG_* */ - u32 cap; /* cap to use */ - u32 cap2; /* cap2 to use */ - u32 port_map; /* port map to use */ - u32 saved_cap; /* saved initial cap */ - u32 saved_cap2; /* saved initial cap2 */ - u32 saved_port_map; /* saved initial port_map */ - u32 em_loc; /* enclosure management location */ }; -struct ahci_port_priv { - struct ata_link *active_link; - struct ahci_cmd_hdr *cmd_slot; - dma_addr_t cmd_slot_dma; - void *cmd_tbl; - dma_addr_t cmd_tbl_dma; - void *rx_fis; - dma_addr_t rx_fis_dma; - /* for NCQ spurious interrupt analysis */ - unsigned int ncq_saw_d2h:1; - unsigned int ncq_saw_dmas:1; - unsigned int ncq_saw_sdb:1; - u32 intr_mask; /* interrupts to enable */ - bool fbs_supported; /* set iff FBS is supported */ - bool fbs_enabled; /* set iff FBS is enabled */ - int fbs_last_dev; /* save FBS.DEV of last FIS */ - /* enclosure management info per PM slot */ - struct ahci_em_priv em_priv[EM_MAX_SLOTS]; +enum board_ids { + /* board IDs by feature in alphabetical order */ + board_ahci, + board_ahci_ign_iferr, + board_ahci_nosntf, + + /* board IDs for specific chipsets in alphabetical order */ + board_ahci_mcp65, + board_ahci_mcp77, + board_ahci_mcp89, + board_ahci_mv, + board_ahci_sb600, + board_ahci_sb700, /* for SB700 and SB800 */ + board_ahci_vt8251, + + /* aliases */ + board_ahci_mcp_linux = board_ahci_mcp65, + board_ahci_mcp67 = board_ahci_mcp65, + board_ahci_mcp73 = board_ahci_mcp65, + board_ahci_mcp79 = board_ahci_mcp77, }; -static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); -static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); -static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc); -static int ahci_port_start(struct ata_port *ap); -static void ahci_port_stop(struct ata_port *ap); -static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc); -static void ahci_qc_prep(struct ata_queued_cmd *qc); -static void ahci_freeze(struct ata_port *ap); -static void ahci_thaw(struct ata_port *ap); -static void ahci_enable_fbs(struct ata_port *ap); -static void ahci_disable_fbs(struct ata_port *ap); -static void ahci_pmp_attach(struct ata_port *ap); -static void ahci_pmp_detach(struct ata_port *ap); -static int ahci_softreset(struct ata_link *link, unsigned int *class, - unsigned long deadline); static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline); -static int ahci_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline); static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); -static void ahci_postreset(struct ata_link *link, unsigned int *class); -static void ahci_error_handler(struct ata_port *ap); -static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); -static int ahci_port_resume(struct ata_port *ap); -static void ahci_dev_config(struct ata_device *dev); -static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, - u32 opts); #ifdef CONFIG_PM -static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int ahci_pci_device_resume(struct pci_dev *pdev); #endif -static ssize_t ahci_activity_show(struct ata_device *dev, char *buf); -static ssize_t ahci_activity_store(struct ata_device *dev, - enum sw_activity val); -static void ahci_init_sw_activity(struct ata_link *link); - -static ssize_t ahci_show_host_caps(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t ahci_show_host_cap2(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t ahci_show_host_version(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t ahci_show_port_cmd(struct device *dev, - struct device_attribute *attr, char *buf); - -static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL); -static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL); -static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL); -static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL); - -static struct device_attribute *ahci_shost_attrs[] = { - &dev_attr_link_power_management_policy, - &dev_attr_em_message_type, - &dev_attr_em_message, - &dev_attr_ahci_host_caps, - &dev_attr_ahci_host_cap2, - &dev_attr_ahci_host_version, - &dev_attr_ahci_port_cmd, - NULL -}; - -static struct device_attribute *ahci_sdev_attrs[] = { - &dev_attr_sw_activity, - &dev_attr_unload_heads, - NULL -}; - -static struct scsi_host_template ahci_sht = { - ATA_NCQ_SHT(DRV_NAME), - .can_queue = AHCI_MAX_CMDS - 1, - .sg_tablesize = AHCI_MAX_SG, - .dma_boundary = AHCI_DMA_BOUNDARY, - .shost_attrs = ahci_shost_attrs, - .sdev_attrs = ahci_sdev_attrs, -}; - -static struct ata_port_operations ahci_ops = { - .inherits = &sata_pmp_port_ops, - - .qc_defer = ahci_pmp_qc_defer, - .qc_prep = ahci_qc_prep, - .qc_issue = ahci_qc_issue, - .qc_fill_rtf = ahci_qc_fill_rtf, - - .freeze = ahci_freeze, - .thaw = ahci_thaw, - .softreset = ahci_softreset, - .hardreset = ahci_hardreset, - .postreset = ahci_postreset, - .pmp_softreset = ahci_softreset, - .error_handler = ahci_error_handler, - .post_internal_cmd = ahci_post_internal_cmd, - .dev_config = ahci_dev_config, - - .scr_read = ahci_scr_read, - .scr_write = ahci_scr_write, - .pmp_attach = ahci_pmp_attach, - .pmp_detach = ahci_pmp_detach, - - .enable_pm = ahci_enable_alpm, - .disable_pm = ahci_disable_alpm, - .em_show = ahci_led_show, - .em_store = ahci_led_store, - .sw_activity_show = ahci_activity_show, - .sw_activity_store = ahci_activity_store, -#ifdef CONFIG_PM - .port_suspend = ahci_port_suspend, - .port_resume = ahci_port_resume, -#endif - .port_start = ahci_port_start, - .port_stop = ahci_port_stop, -}; static struct ata_port_operations ahci_vt8251_ops = { .inherits = &ahci_ops, @@ -463,6 +108,7 @@ static struct ata_port_operations ahci_sb600_ops = { #define AHCI_HFLAGS(flags) .private_data = (void *)(flags) static const struct ata_port_info ahci_port_info[] = { + /* by features */ [board_ahci] = { .flags = AHCI_FLAG_COMMON, @@ -470,81 +116,83 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_vt8251] = + [board_ahci_ign_iferr] = { - AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP), + AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &ahci_vt8251_ops, + .port_ops = &ahci_ops, }, - [board_ahci_ign_iferr] = + [board_ahci_nosntf] = { - AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR), + AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_sb600] = + /* by chipsets */ + [board_ahci_mcp65] = { - AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | - AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255 | - AHCI_HFLAG_32BIT_ONLY), + AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | + AHCI_HFLAG_YES_NCQ), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &ahci_sb600_ops, + .port_ops = &ahci_ops, }, - [board_ahci_mv] = + [board_ahci_mcp77] = { - AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | - AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP), - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, + AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP), + .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_sb700] = /* for SB700 and SB800 */ + [board_ahci_mcp89] = { - AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL), + AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &ahci_sb600_ops, + .port_ops = &ahci_ops, }, - [board_ahci_mcp65] = + [board_ahci_mv] = { - AHCI_HFLAGS (AHCI_HFLAG_YES_NCQ), - .flags = AHCI_FLAG_COMMON, + AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | + AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP), + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - [board_ahci_nopmp] = + [board_ahci_sb600] = { - AHCI_HFLAGS (AHCI_HFLAG_NO_PMP), + AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | + AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255 | + AHCI_HFLAG_32BIT_ONLY), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &ahci_ops, + .port_ops = &ahci_sb600_ops, }, - [board_ahci_yesncq] = + [board_ahci_sb700] = /* for SB700 and SB800 */ { - AHCI_HFLAGS (AHCI_HFLAG_YES_NCQ), + AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &ahci_ops, + .port_ops = &ahci_sb600_ops, }, - [board_ahci_nosntf] = + [board_ahci_vt8251] = { - AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF), + AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &ahci_ops, + .port_ops = &ahci_vt8251_ops, }, }; @@ -629,82 +277,82 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 }, /* MCP65 */ - { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_yesncq }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0581), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0582), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0583), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0584), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0585), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0586), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0587), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0588), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x0589), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x058a), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x058b), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x058c), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x058d), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x058e), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x058f), board_ahci_yesncq }, /* Linux ID */ - { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_yesncq }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci }, /* MCP77 */ - { PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci }, /* MCP79 */ - { PCI_VDEVICE(NVIDIA, 0x0d84), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d85), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d86), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d87), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d88), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d89), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d8a), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d8b), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d8c), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d8d), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d8e), board_ahci }, /* MCP89 */ - { PCI_VDEVICE(NVIDIA, 0x0d8f), board_ahci }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_mcp67 }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0581), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0582), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0583), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0584), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0585), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0586), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0587), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0588), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x0589), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x058a), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x058b), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x058c), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x058d), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x058e), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x058f), board_ahci_mcp_linux }, /* Linux ID */ + { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_mcp73 }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci_mcp77 }, /* MCP77 */ + { PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci_mcp79 }, /* MCP79 */ + { PCI_VDEVICE(NVIDIA, 0x0d84), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d85), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d86), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d87), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d88), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d89), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d8a), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d8b), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d8c), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d8d), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d8e), board_ahci_mcp89 }, /* MCP89 */ + { PCI_VDEVICE(NVIDIA, 0x0d8f), board_ahci_mcp89 }, /* MCP89 */ /* SiS */ { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */ @@ -737,12 +385,6 @@ static struct pci_driver ahci_pci_driver = { #endif }; -static int ahci_em_messages = 1; -module_param(ahci_em_messages, int, 0444); -/* add other LED protocol types when they become supported */ -MODULE_PARM_DESC(ahci_em_messages, - "Set AHCI Enclosure Management Message type (0 = disabled, 1 = LED"); - #if defined(CONFIG_PATA_MARVELL) || defined(CONFIG_PATA_MARVELL_MODULE) static int marvell_enable; #else @@ -752,166 +394,15 @@ module_param(marvell_enable, int, 0644); MODULE_PARM_DESC(marvell_enable, "Marvell SATA via AHCI (1 = enabled)"); -static inline int ahci_nr_ports(u32 cap) -{ - return (cap & 0x1f) + 1; -} - -static inline void __iomem *__ahci_port_base(struct ata_host *host, - unsigned int port_no) -{ - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - - return mmio + 0x100 + (port_no * 0x80); -} - -static inline void __iomem *ahci_port_base(struct ata_port *ap) -{ - return __ahci_port_base(ap->host, ap->port_no); -} - -static void ahci_enable_ahci(void __iomem *mmio) -{ - int i; - u32 tmp; - - /* turn on AHCI_EN */ - tmp = readl(mmio + HOST_CTL); - if (tmp & HOST_AHCI_EN) - return; - - /* Some controllers need AHCI_EN to be written multiple times. - * Try a few times before giving up. - */ - for (i = 0; i < 5; i++) { - tmp |= HOST_AHCI_EN; - writel(tmp, mmio + HOST_CTL); - tmp = readl(mmio + HOST_CTL); /* flush && sanity check */ - if (tmp & HOST_AHCI_EN) - return; - msleep(10); - } - - WARN_ON(1); -} - -static ssize_t ahci_show_host_caps(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(dev); - struct ata_port *ap = ata_shost_to_port(shost); - struct ahci_host_priv *hpriv = ap->host->private_data; - - return sprintf(buf, "%x\n", hpriv->cap); -} - -static ssize_t ahci_show_host_cap2(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(dev); - struct ata_port *ap = ata_shost_to_port(shost); - struct ahci_host_priv *hpriv = ap->host->private_data; - - return sprintf(buf, "%x\n", hpriv->cap2); -} - -static ssize_t ahci_show_host_version(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(dev); - struct ata_port *ap = ata_shost_to_port(shost); - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - - return sprintf(buf, "%x\n", readl(mmio + HOST_VERSION)); -} - -static ssize_t ahci_show_port_cmd(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(dev); - struct ata_port *ap = ata_shost_to_port(shost); - void __iomem *port_mmio = ahci_port_base(ap); - - return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD)); -} - -/** - * ahci_save_initial_config - Save and fixup initial config values - * @pdev: target PCI device - * @hpriv: host private area to store config values - * - * Some registers containing configuration info might be setup by - * BIOS and might be cleared on reset. This function saves the - * initial values of those registers into @hpriv such that they - * can be restored after controller reset. - * - * If inconsistent, config values are fixed up by this function. - * - * LOCKING: - * None. - */ -static void ahci_save_initial_config(struct pci_dev *pdev, - struct ahci_host_priv *hpriv) +static void ahci_pci_save_initial_config(struct pci_dev *pdev, + struct ahci_host_priv *hpriv) { - void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; - u32 cap, cap2, vers, port_map; - int i; - int mv; - - /* make sure AHCI mode is enabled before accessing CAP */ - ahci_enable_ahci(mmio); + unsigned int force_port_map = 0; + unsigned int mask_port_map = 0; - /* Values prefixed with saved_ are written back to host after - * reset. Values without are used for driver operation. - */ - hpriv->saved_cap = cap = readl(mmio + HOST_CAP); - hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); - - /* CAP2 register is only defined for AHCI 1.2 and later */ - vers = readl(mmio + HOST_VERSION); - if ((vers >> 16) > 1 || - ((vers >> 16) == 1 && (vers & 0xFFFF) >= 0x200)) - hpriv->saved_cap2 = cap2 = readl(mmio + HOST_CAP2); - else - hpriv->saved_cap2 = cap2 = 0; - - /* some chips have errata preventing 64bit use */ - if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) { - dev_printk(KERN_INFO, &pdev->dev, - "controller can't do 64bit DMA, forcing 32bit\n"); - cap &= ~HOST_CAP_64; - } - - if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) { - dev_printk(KERN_INFO, &pdev->dev, - "controller can't do NCQ, turning off CAP_NCQ\n"); - cap &= ~HOST_CAP_NCQ; - } - - if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) { - dev_printk(KERN_INFO, &pdev->dev, - "controller can do NCQ, turning on CAP_NCQ\n"); - cap |= HOST_CAP_NCQ; - } - - if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) { - dev_printk(KERN_INFO, &pdev->dev, - "controller can't do PMP, turning off CAP_PMP\n"); - cap &= ~HOST_CAP_PMP; - } - - if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) { - dev_printk(KERN_INFO, &pdev->dev, - "controller can't do SNTF, turning off CAP_SNTF\n"); - cap &= ~HOST_CAP_SNTF; - } - - if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361 && - port_map != 1) { - dev_printk(KERN_INFO, &pdev->dev, - "JMB361 has only one port, port_map 0x%x -> 0x%x\n", - port_map, 1); - port_map = 1; + if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) { + dev_info(&pdev->dev, "JMB361 has only one port\n"); + force_port_map = 1; } /* @@ -921,469 +412,25 @@ static void ahci_save_initial_config(struct pci_dev *pdev, */ if (hpriv->flags & AHCI_HFLAG_MV_PATA) { if (pdev->device == 0x6121) - mv = 0x3; + mask_port_map = 0x3; else - mv = 0xf; - dev_printk(KERN_ERR, &pdev->dev, - "MV_AHCI HACK: port_map %x -> %x\n", - port_map, - port_map & mv); - dev_printk(KERN_ERR, &pdev->dev, + mask_port_map = 0xf; + dev_info(&pdev->dev, "Disabling your PATA port. Use the boot option 'ahci.marvell_enable=0' to avoid this.\n"); - - port_map &= mv; } - /* cross check port_map and cap.n_ports */ - if (port_map) { - int map_ports = 0; - - for (i = 0; i < AHCI_MAX_PORTS; i++) - if (port_map & (1 << i)) - map_ports++; - - /* If PI has more ports than n_ports, whine, clear - * port_map and let it be generated from n_ports. - */ - if (map_ports > ahci_nr_ports(cap)) { - dev_printk(KERN_WARNING, &pdev->dev, - "implemented port map (0x%x) contains more " - "ports than nr_ports (%u), using nr_ports\n", - port_map, ahci_nr_ports(cap)); - port_map = 0; - } - } - - /* fabricate port_map from cap.nr_ports */ - if (!port_map) { - port_map = (1 << ahci_nr_ports(cap)) - 1; - dev_printk(KERN_WARNING, &pdev->dev, - "forcing PORTS_IMPL to 0x%x\n", port_map); - - /* write the fixed up value to the PI register */ - hpriv->saved_port_map = port_map; - } - - /* record values to use during operation */ - hpriv->cap = cap; - hpriv->cap2 = cap2; - hpriv->port_map = port_map; -} - -/** - * ahci_restore_initial_config - Restore initial config - * @host: target ATA host - * - * Restore initial config stored by ahci_save_initial_config(). - * - * LOCKING: - * None. - */ -static void ahci_restore_initial_config(struct ata_host *host) -{ - struct ahci_host_priv *hpriv = host->private_data; - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - - writel(hpriv->saved_cap, mmio + HOST_CAP); - if (hpriv->saved_cap2) - writel(hpriv->saved_cap2, mmio + HOST_CAP2); - writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL); - (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ -} - -static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg) -{ - static const int offset[] = { - [SCR_STATUS] = PORT_SCR_STAT, - [SCR_CONTROL] = PORT_SCR_CTL, - [SCR_ERROR] = PORT_SCR_ERR, - [SCR_ACTIVE] = PORT_SCR_ACT, - [SCR_NOTIFICATION] = PORT_SCR_NTF, - }; - struct ahci_host_priv *hpriv = ap->host->private_data; - - if (sc_reg < ARRAY_SIZE(offset) && - (sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF))) - return offset[sc_reg]; - return 0; -} - -static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) -{ - void __iomem *port_mmio = ahci_port_base(link->ap); - int offset = ahci_scr_offset(link->ap, sc_reg); - - if (offset) { - *val = readl(port_mmio + offset); - return 0; - } - return -EINVAL; -} - -static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) -{ - void __iomem *port_mmio = ahci_port_base(link->ap); - int offset = ahci_scr_offset(link->ap, sc_reg); - - if (offset) { - writel(val, port_mmio + offset); - return 0; - } - return -EINVAL; -} - -static void ahci_start_engine(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - u32 tmp; - - /* start DMA */ - tmp = readl(port_mmio + PORT_CMD); - tmp |= PORT_CMD_START; - writel(tmp, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ -} - -static int ahci_stop_engine(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - u32 tmp; - - tmp = readl(port_mmio + PORT_CMD); - - /* check if the HBA is idle */ - if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) - return 0; - - /* setting HBA to idle */ - tmp &= ~PORT_CMD_START; - writel(tmp, port_mmio + PORT_CMD); - - /* wait for engine to stop. This could be as long as 500 msec */ - tmp = ata_wait_register(port_mmio + PORT_CMD, - PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); - if (tmp & PORT_CMD_LIST_ON) - return -EIO; - - return 0; -} - -static void ahci_start_fis_rx(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - struct ahci_host_priv *hpriv = ap->host->private_data; - struct ahci_port_priv *pp = ap->private_data; - u32 tmp; - - /* set FIS registers */ - if (hpriv->cap & HOST_CAP_64) - writel((pp->cmd_slot_dma >> 16) >> 16, - port_mmio + PORT_LST_ADDR_HI); - writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); - - if (hpriv->cap & HOST_CAP_64) - writel((pp->rx_fis_dma >> 16) >> 16, - port_mmio + PORT_FIS_ADDR_HI); - writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); - - /* enable FIS reception */ - tmp = readl(port_mmio + PORT_CMD); - tmp |= PORT_CMD_FIS_RX; - writel(tmp, port_mmio + PORT_CMD); - - /* flush */ - readl(port_mmio + PORT_CMD); + ahci_save_initial_config(&pdev->dev, hpriv, force_port_map, + mask_port_map); } -static int ahci_stop_fis_rx(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - u32 tmp; - - /* disable FIS reception */ - tmp = readl(port_mmio + PORT_CMD); - tmp &= ~PORT_CMD_FIS_RX; - writel(tmp, port_mmio + PORT_CMD); - - /* wait for completion, spec says 500ms, give it 1000 */ - tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_FIS_ON, - PORT_CMD_FIS_ON, 10, 1000); - if (tmp & PORT_CMD_FIS_ON) - return -EBUSY; - - return 0; -} - -static void ahci_power_up(struct ata_port *ap) -{ - struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 cmd; - - cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; - - /* spin up device */ - if (hpriv->cap & HOST_CAP_SSS) { - cmd |= PORT_CMD_SPIN_UP; - writel(cmd, port_mmio + PORT_CMD); - } - - /* wake up link */ - writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); -} - -static void ahci_disable_alpm(struct ata_port *ap) -{ - struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 cmd; - struct ahci_port_priv *pp = ap->private_data; - - /* IPM bits should be disabled by libata-core */ - /* get the existing command bits */ - cmd = readl(port_mmio + PORT_CMD); - - /* disable ALPM and ASP */ - cmd &= ~PORT_CMD_ASP; - cmd &= ~PORT_CMD_ALPE; - - /* force the interface back to active */ - cmd |= PORT_CMD_ICC_ACTIVE; - - /* write out new cmd value */ - writel(cmd, port_mmio + PORT_CMD); - cmd = readl(port_mmio + PORT_CMD); - - /* wait 10ms to be sure we've come out of any low power state */ - msleep(10); - - /* clear out any PhyRdy stuff from interrupt status */ - writel(PORT_IRQ_PHYRDY, port_mmio + PORT_IRQ_STAT); - - /* go ahead and clean out PhyRdy Change from Serror too */ - ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18))); - - /* - * Clear flag to indicate that we should ignore all PhyRdy - * state changes - */ - hpriv->flags &= ~AHCI_HFLAG_NO_HOTPLUG; - - /* - * Enable interrupts on Phy Ready. - */ - pp->intr_mask |= PORT_IRQ_PHYRDY; - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); - - /* - * don't change the link pm policy - we can be called - * just to turn of link pm temporarily - */ -} - -static int ahci_enable_alpm(struct ata_port *ap, - enum link_pm policy) -{ - struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 cmd; - struct ahci_port_priv *pp = ap->private_data; - u32 asp; - - /* Make sure the host is capable of link power management */ - if (!(hpriv->cap & HOST_CAP_ALPM)) - return -EINVAL; - - switch (policy) { - case MAX_PERFORMANCE: - case NOT_AVAILABLE: - /* - * if we came here with NOT_AVAILABLE, - * it just means this is the first time we - * have tried to enable - default to max performance, - * and let the user go to lower power modes on request. - */ - ahci_disable_alpm(ap); - return 0; - case MIN_POWER: - /* configure HBA to enter SLUMBER */ - asp = PORT_CMD_ASP; - break; - case MEDIUM_POWER: - /* configure HBA to enter PARTIAL */ - asp = 0; - break; - default: - return -EINVAL; - } - - /* - * Disable interrupts on Phy Ready. This keeps us from - * getting woken up due to spurious phy ready interrupts - * TBD - Hot plug should be done via polling now, is - * that even supported? - */ - pp->intr_mask &= ~PORT_IRQ_PHYRDY; - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); - - /* - * Set a flag to indicate that we should ignore all PhyRdy - * state changes since these can happen now whenever we - * change link state - */ - hpriv->flags |= AHCI_HFLAG_NO_HOTPLUG; - - /* get the existing command bits */ - cmd = readl(port_mmio + PORT_CMD); - - /* - * Set ASP based on Policy - */ - cmd |= asp; - - /* - * Setting this bit will instruct the HBA to aggressively - * enter a lower power link state when it's appropriate and - * based on the value set above for ASP - */ - cmd |= PORT_CMD_ALPE; - - /* write out new cmd value */ - writel(cmd, port_mmio + PORT_CMD); - cmd = readl(port_mmio + PORT_CMD); - - /* IPM bits should be set by libata-core */ - return 0; -} - -#ifdef CONFIG_PM -static void ahci_power_down(struct ata_port *ap) -{ - struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 cmd, scontrol; - - if (!(hpriv->cap & HOST_CAP_SSS)) - return; - - /* put device into listen mode, first set PxSCTL.DET to 0 */ - scontrol = readl(port_mmio + PORT_SCR_CTL); - scontrol &= ~0xf; - writel(scontrol, port_mmio + PORT_SCR_CTL); - - /* then set PxCMD.SUD to 0 */ - cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; - cmd &= ~PORT_CMD_SPIN_UP; - writel(cmd, port_mmio + PORT_CMD); -} -#endif - -static void ahci_start_port(struct ata_port *ap) -{ - struct ahci_port_priv *pp = ap->private_data; - struct ata_link *link; - struct ahci_em_priv *emp; - ssize_t rc; - int i; - - /* enable FIS reception */ - ahci_start_fis_rx(ap); - - /* enable DMA */ - ahci_start_engine(ap); - - /* turn on LEDs */ - if (ap->flags & ATA_FLAG_EM) { - ata_for_each_link(link, ap, EDGE) { - emp = &pp->em_priv[link->pmp]; - - /* EM Transmit bit maybe busy during init */ - for (i = 0; i < EM_MAX_RETRY; i++) { - rc = ahci_transmit_led_message(ap, - emp->led_state, - 4); - if (rc == -EBUSY) - msleep(1); - else - break; - } - } - } - - if (ap->flags & ATA_FLAG_SW_ACTIVITY) - ata_for_each_link(link, ap, EDGE) - ahci_init_sw_activity(link); - -} - -static int ahci_deinit_port(struct ata_port *ap, const char **emsg) -{ - int rc; - - /* disable DMA */ - rc = ahci_stop_engine(ap); - if (rc) { - *emsg = "failed to stop engine"; - return rc; - } - - /* disable FIS reception */ - rc = ahci_stop_fis_rx(ap); - if (rc) { - *emsg = "failed stop FIS RX"; - return rc; - } - - return 0; -} - -static int ahci_reset_controller(struct ata_host *host) +static int ahci_pci_reset_controller(struct ata_host *host) { struct pci_dev *pdev = to_pci_dev(host->dev); - struct ahci_host_priv *hpriv = host->private_data; - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - u32 tmp; - /* we must be in AHCI mode, before using anything - * AHCI-specific, such as HOST_RESET. - */ - ahci_enable_ahci(mmio); - - /* global controller reset */ - if (!ahci_skip_host_reset) { - tmp = readl(mmio + HOST_CTL); - if ((tmp & HOST_RESET) == 0) { - writel(tmp | HOST_RESET, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - } - - /* - * to perform host reset, OS should set HOST_RESET - * and poll until this bit is read to be "0". - * reset must complete within 1 second, or - * the hardware should be considered fried. - */ - tmp = ata_wait_register(mmio + HOST_CTL, HOST_RESET, - HOST_RESET, 10, 1000); - - if (tmp & HOST_RESET) { - dev_printk(KERN_ERR, host->dev, - "controller reset failed (0x%x)\n", tmp); - return -EIO; - } - - /* turn on AHCI mode */ - ahci_enable_ahci(mmio); - - /* Some registers might be cleared on reset. Restore - * initial values. - */ - ahci_restore_initial_config(host); - } else - dev_printk(KERN_INFO, host->dev, - "skipping global host reset\n"); + ahci_reset_controller(host); if (pdev->vendor == PCI_VENDOR_ID_INTEL) { + struct ahci_host_priv *hpriv = host->private_data; u16 tmp16; /* configure PCS */ @@ -1397,267 +444,10 @@ static int ahci_reset_controller(struct ata_host *host) return 0; } -static void ahci_sw_activity(struct ata_link *link) -{ - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; - struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; - - if (!(link->flags & ATA_LFLAG_SW_ACTIVITY)) - return; - - emp->activity++; - if (!timer_pending(&emp->timer)) - mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10)); -} - -static void ahci_sw_activity_blink(unsigned long arg) -{ - struct ata_link *link = (struct ata_link *)arg; - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; - struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; - unsigned long led_message = emp->led_state; - u32 activity_led_state; - unsigned long flags; - - led_message &= EM_MSG_LED_VALUE; - led_message |= ap->port_no | (link->pmp << 8); - - /* check to see if we've had activity. If so, - * toggle state of LED and reset timer. If not, - * turn LED to desired idle state. - */ - spin_lock_irqsave(ap->lock, flags); - if (emp->saved_activity != emp->activity) { - emp->saved_activity = emp->activity; - /* get the current LED state */ - activity_led_state = led_message & EM_MSG_LED_VALUE_ON; - - if (activity_led_state) - activity_led_state = 0; - else - activity_led_state = 1; - - /* clear old state */ - led_message &= ~EM_MSG_LED_VALUE_ACTIVITY; - - /* toggle state */ - led_message |= (activity_led_state << 16); - mod_timer(&emp->timer, jiffies + msecs_to_jiffies(100)); - } else { - /* switch to idle */ - led_message &= ~EM_MSG_LED_VALUE_ACTIVITY; - if (emp->blink_policy == BLINK_OFF) - led_message |= (1 << 16); - } - spin_unlock_irqrestore(ap->lock, flags); - ahci_transmit_led_message(ap, led_message, 4); -} - -static void ahci_init_sw_activity(struct ata_link *link) -{ - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; - struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; - - /* init activity stats, setup timer */ - emp->saved_activity = emp->activity = 0; - setup_timer(&emp->timer, ahci_sw_activity_blink, (unsigned long)link); - - /* check our blink policy and set flag for link if it's enabled */ - if (emp->blink_policy) - link->flags |= ATA_LFLAG_SW_ACTIVITY; -} - -static int ahci_reset_em(struct ata_host *host) -{ - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - u32 em_ctl; - - em_ctl = readl(mmio + HOST_EM_CTL); - if ((em_ctl & EM_CTL_TM) || (em_ctl & EM_CTL_RST)) - return -EINVAL; - - writel(em_ctl | EM_CTL_RST, mmio + HOST_EM_CTL); - return 0; -} - -static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, - ssize_t size) -{ - struct ahci_host_priv *hpriv = ap->host->private_data; - struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - u32 em_ctl; - u32 message[] = {0, 0}; - unsigned long flags; - int pmp; - struct ahci_em_priv *emp; - - /* get the slot number from the message */ - pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; - if (pmp < EM_MAX_SLOTS) - emp = &pp->em_priv[pmp]; - else - return -EINVAL; - - spin_lock_irqsave(ap->lock, flags); - - /* - * if we are still busy transmitting a previous message, - * do not allow - */ - em_ctl = readl(mmio + HOST_EM_CTL); - if (em_ctl & EM_CTL_TM) { - spin_unlock_irqrestore(ap->lock, flags); - return -EBUSY; - } - - /* - * create message header - this is all zero except for - * the message size, which is 4 bytes. - */ - message[0] |= (4 << 8); - - /* ignore 0:4 of byte zero, fill in port info yourself */ - message[1] = ((state & ~EM_MSG_LED_HBA_PORT) | ap->port_no); - - /* write message to EM_LOC */ - writel(message[0], mmio + hpriv->em_loc); - writel(message[1], mmio + hpriv->em_loc+4); - - /* save off new led state for port/slot */ - emp->led_state = state; - - /* - * tell hardware to transmit the message - */ - writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); - - spin_unlock_irqrestore(ap->lock, flags); - return size; -} - -static ssize_t ahci_led_show(struct ata_port *ap, char *buf) -{ - struct ahci_port_priv *pp = ap->private_data; - struct ata_link *link; - struct ahci_em_priv *emp; - int rc = 0; - - ata_for_each_link(link, ap, EDGE) { - emp = &pp->em_priv[link->pmp]; - rc += sprintf(buf, "%lx\n", emp->led_state); - } - return rc; -} - -static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, - size_t size) -{ - int state; - int pmp; - struct ahci_port_priv *pp = ap->private_data; - struct ahci_em_priv *emp; - - state = simple_strtoul(buf, NULL, 0); - - /* get the slot number from the message */ - pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; - if (pmp < EM_MAX_SLOTS) - emp = &pp->em_priv[pmp]; - else - return -EINVAL; - - /* mask off the activity bits if we are in sw_activity - * mode, user should turn off sw_activity before setting - * activity led through em_message - */ - if (emp->blink_policy) - state &= ~EM_MSG_LED_VALUE_ACTIVITY; - - return ahci_transmit_led_message(ap, state, size); -} - -static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) -{ - struct ata_link *link = dev->link; - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; - struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; - u32 port_led_state = emp->led_state; - - /* save the desired Activity LED behavior */ - if (val == OFF) { - /* clear LFLAG */ - link->flags &= ~(ATA_LFLAG_SW_ACTIVITY); - - /* set the LED to OFF */ - port_led_state &= EM_MSG_LED_VALUE_OFF; - port_led_state |= (ap->port_no | (link->pmp << 8)); - ahci_transmit_led_message(ap, port_led_state, 4); - } else { - link->flags |= ATA_LFLAG_SW_ACTIVITY; - if (val == BLINK_OFF) { - /* set LED to ON for idle */ - port_led_state &= EM_MSG_LED_VALUE_OFF; - port_led_state |= (ap->port_no | (link->pmp << 8)); - port_led_state |= EM_MSG_LED_VALUE_ON; /* check this */ - ahci_transmit_led_message(ap, port_led_state, 4); - } - } - emp->blink_policy = val; - return 0; -} - -static ssize_t ahci_activity_show(struct ata_device *dev, char *buf) -{ - struct ata_link *link = dev->link; - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; - struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; - - /* display the saved value of activity behavior for this - * disk. - */ - return sprintf(buf, "%d\n", emp->blink_policy); -} - -static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap, - int port_no, void __iomem *mmio, - void __iomem *port_mmio) -{ - const char *emsg = NULL; - int rc; - u32 tmp; - - /* make sure port is not active */ - rc = ahci_deinit_port(ap, &emsg); - if (rc) - dev_printk(KERN_WARNING, &pdev->dev, - "%s (%d)\n", emsg, rc); - - /* clear SError */ - tmp = readl(port_mmio + PORT_SCR_ERR); - VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); - writel(tmp, port_mmio + PORT_SCR_ERR); - - /* clear port IRQ */ - tmp = readl(port_mmio + PORT_IRQ_STAT); - VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); - if (tmp) - writel(tmp, port_mmio + PORT_IRQ_STAT); - - writel(1 << port_no, mmio + HOST_IRQ_STAT); -} - -static void ahci_init_controller(struct ata_host *host) +static void ahci_pci_init_controller(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; struct pci_dev *pdev = to_pci_dev(host->dev); - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - int i; void __iomem *port_mmio; u32 tmp; int mv; @@ -1678,220 +468,7 @@ static void ahci_init_controller(struct ata_host *host) writel(tmp, port_mmio + PORT_IRQ_STAT); } - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - - port_mmio = ahci_port_base(ap); - if (ata_port_is_dummy(ap)) - continue; - - ahci_port_init(pdev, ap, i, mmio, port_mmio); - } - - tmp = readl(mmio + HOST_CTL); - VPRINTK("HOST_CTL 0x%x\n", tmp); - writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); - tmp = readl(mmio + HOST_CTL); - VPRINTK("HOST_CTL 0x%x\n", tmp); -} - -static void ahci_dev_config(struct ata_device *dev) -{ - struct ahci_host_priv *hpriv = dev->link->ap->host->private_data; - - if (hpriv->flags & AHCI_HFLAG_SECT255) { - dev->max_sectors = 255; - ata_dev_printk(dev, KERN_INFO, - "SB600 AHCI: limiting to 255 sectors per cmd\n"); - } -} - -static unsigned int ahci_dev_classify(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - struct ata_taskfile tf; - u32 tmp; - - tmp = readl(port_mmio + PORT_SIG); - tf.lbah = (tmp >> 24) & 0xff; - tf.lbam = (tmp >> 16) & 0xff; - tf.lbal = (tmp >> 8) & 0xff; - tf.nsect = (tmp) & 0xff; - - return ata_dev_classify(&tf); -} - -static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, - u32 opts) -{ - dma_addr_t cmd_tbl_dma; - - cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ; - - pp->cmd_slot[tag].opts = cpu_to_le32(opts); - pp->cmd_slot[tag].status = 0; - pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff); - pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16); -} - -static int ahci_kick_engine(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - struct ahci_host_priv *hpriv = ap->host->private_data; - u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF; - u32 tmp; - int busy, rc; - - /* stop engine */ - rc = ahci_stop_engine(ap); - if (rc) - goto out_restart; - - /* need to do CLO? - * always do CLO if PMP is attached (AHCI-1.3 9.2) - */ - busy = status & (ATA_BUSY | ATA_DRQ); - if (!busy && !sata_pmp_attached(ap)) { - rc = 0; - goto out_restart; - } - - if (!(hpriv->cap & HOST_CAP_CLO)) { - rc = -EOPNOTSUPP; - goto out_restart; - } - - /* perform CLO */ - tmp = readl(port_mmio + PORT_CMD); - tmp |= PORT_CMD_CLO; - writel(tmp, port_mmio + PORT_CMD); - - rc = 0; - tmp = ata_wait_register(port_mmio + PORT_CMD, - PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); - if (tmp & PORT_CMD_CLO) - rc = -EIO; - - /* restart engine */ - out_restart: - ahci_start_engine(ap); - return rc; -} - -static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, - struct ata_taskfile *tf, int is_cmd, u16 flags, - unsigned long timeout_msec) -{ - const u32 cmd_fis_len = 5; /* five dwords */ - struct ahci_port_priv *pp = ap->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u8 *fis = pp->cmd_tbl; - u32 tmp; - - /* prep the command */ - ata_tf_to_fis(tf, pmp, is_cmd, fis); - ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12)); - - /* issue & wait */ - writel(1, port_mmio + PORT_CMD_ISSUE); - - if (timeout_msec) { - tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, - 1, timeout_msec); - if (tmp & 0x1) { - ahci_kick_engine(ap); - return -EBUSY; - } - } else - readl(port_mmio + PORT_CMD_ISSUE); /* flush */ - - return 0; -} - -static int ahci_do_softreset(struct ata_link *link, unsigned int *class, - int pmp, unsigned long deadline, - int (*check_ready)(struct ata_link *link)) -{ - struct ata_port *ap = link->ap; - struct ahci_host_priv *hpriv = ap->host->private_data; - const char *reason = NULL; - unsigned long now, msecs; - struct ata_taskfile tf; - int rc; - - DPRINTK("ENTER\n"); - - /* prepare for SRST (AHCI-1.1 10.4.1) */ - rc = ahci_kick_engine(ap); - if (rc && rc != -EOPNOTSUPP) - ata_link_printk(link, KERN_WARNING, - "failed to reset engine (errno=%d)\n", rc); - - ata_tf_init(link->device, &tf); - - /* issue the first D2H Register FIS */ - msecs = 0; - now = jiffies; - if (time_after(now, deadline)) - msecs = jiffies_to_msecs(deadline - now); - - tf.ctl |= ATA_SRST; - if (ahci_exec_polled_cmd(ap, pmp, &tf, 0, - AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) { - rc = -EIO; - reason = "1st FIS failed"; - goto fail; - } - - /* spec says at least 5us, but be generous and sleep for 1ms */ - msleep(1); - - /* issue the second D2H Register FIS */ - tf.ctl &= ~ATA_SRST; - ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); - - /* wait for link to become ready */ - rc = ata_wait_after_reset(link, deadline, check_ready); - if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) { - /* - * Workaround for cases where link online status can't - * be trusted. Treat device readiness timeout as link - * offline. - */ - ata_link_printk(link, KERN_INFO, - "device not ready, treating as offline\n"); - *class = ATA_DEV_NONE; - } else if (rc) { - /* link occupied, -ENODEV too is an error */ - reason = "device not ready"; - goto fail; - } else - *class = ahci_dev_classify(ap); - - DPRINTK("EXIT, class=%u\n", *class); - return 0; - - fail: - ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason); - return rc; -} - -static int ahci_check_ready(struct ata_link *link) -{ - void __iomem *port_mmio = ahci_port_base(link->ap); - u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF; - - return ata_check_ready(status); -} - -static int ahci_softreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) -{ - int pmp = sata_srst_pmp(link); - - DPRINTK("ENTER\n"); - - return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); + ahci_init_controller(host); } static int ahci_sb600_check_ready(struct ata_link *link) @@ -1943,38 +520,6 @@ static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class, return rc; } -static int ahci_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) -{ - const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); - struct ata_port *ap = link->ap; - struct ahci_port_priv *pp = ap->private_data; - u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; - struct ata_taskfile tf; - bool online; - int rc; - - DPRINTK("ENTER\n"); - - ahci_stop_engine(ap); - - /* clear D2H reception area to properly wait for D2H FIS */ - ata_tf_init(link->device, &tf); - tf.command = 0x80; - ata_tf_to_fis(&tf, 0, 0, d2h_fis); - - rc = sata_link_hardreset(link, timing, deadline, &online, - ahci_check_ready); - - ahci_start_engine(ap); - - if (online) - *class = ahci_dev_classify(ap); - - DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); - return rc; -} - static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { @@ -2043,605 +588,12 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, return rc; } -static void ahci_postreset(struct ata_link *link, unsigned int *class) -{ - struct ata_port *ap = link->ap; - void __iomem *port_mmio = ahci_port_base(ap); - u32 new_tmp, tmp; - - ata_std_postreset(link, class); - - /* Make sure port's ATAPI bit is set appropriately */ - new_tmp = tmp = readl(port_mmio + PORT_CMD); - if (*class == ATA_DEV_ATAPI) - new_tmp |= PORT_CMD_ATAPI; - else - new_tmp &= ~PORT_CMD_ATAPI; - if (new_tmp != tmp) { - writel(new_tmp, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ - } -} - -static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl) -{ - struct scatterlist *sg; - struct ahci_sg *ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ; - unsigned int si; - - VPRINTK("ENTER\n"); - - /* - * Next, the S/G list. - */ - for_each_sg(qc->sg, sg, qc->n_elem, si) { - dma_addr_t addr = sg_dma_address(sg); - u32 sg_len = sg_dma_len(sg); - - ahci_sg[si].addr = cpu_to_le32(addr & 0xffffffff); - ahci_sg[si].addr_hi = cpu_to_le32((addr >> 16) >> 16); - ahci_sg[si].flags_size = cpu_to_le32(sg_len - 1); - } - - return si; -} - -static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct ahci_port_priv *pp = ap->private_data; - - if (!sata_pmp_attached(ap) || pp->fbs_enabled) - return ata_std_qc_defer(qc); - else - return sata_pmp_qc_defer_cmd_switch(qc); -} - -static void ahci_qc_prep(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct ahci_port_priv *pp = ap->private_data; - int is_atapi = ata_is_atapi(qc->tf.protocol); - void *cmd_tbl; - u32 opts; - const u32 cmd_fis_len = 5; /* five dwords */ - unsigned int n_elem; - - /* - * Fill in command table information. First, the header, - * a SATA Register - Host to Device command FIS. - */ - cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ; - - ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, cmd_tbl); - if (is_atapi) { - memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32); - memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len); - } - - n_elem = 0; - if (qc->flags & ATA_QCFLAG_DMAMAP) - n_elem = ahci_fill_sg(qc, cmd_tbl); - - /* - * Fill in command slot information. - */ - opts = cmd_fis_len | n_elem << 16 | (qc->dev->link->pmp << 12); - if (qc->tf.flags & ATA_TFLAG_WRITE) - opts |= AHCI_CMD_WRITE; - if (is_atapi) - opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH; - - ahci_fill_cmd_slot(pp, qc->tag, opts); -} - -static void ahci_fbs_dec_intr(struct ata_port *ap) -{ - struct ahci_port_priv *pp = ap->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 fbs = readl(port_mmio + PORT_FBS); - int retries = 3; - - DPRINTK("ENTER\n"); - BUG_ON(!pp->fbs_enabled); - - /* time to wait for DEC is not specified by AHCI spec, - * add a retry loop for safety. - */ - writel(fbs | PORT_FBS_DEC, port_mmio + PORT_FBS); - fbs = readl(port_mmio + PORT_FBS); - while ((fbs & PORT_FBS_DEC) && retries--) { - udelay(1); - fbs = readl(port_mmio + PORT_FBS); - } - - if (fbs & PORT_FBS_DEC) - dev_printk(KERN_ERR, ap->host->dev, - "failed to clear device error\n"); -} - -static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) -{ - struct ahci_host_priv *hpriv = ap->host->private_data; - struct ahci_port_priv *pp = ap->private_data; - struct ata_eh_info *host_ehi = &ap->link.eh_info; - struct ata_link *link = NULL; - struct ata_queued_cmd *active_qc; - struct ata_eh_info *active_ehi; - bool fbs_need_dec = false; - u32 serror; - - /* determine active link with error */ - if (pp->fbs_enabled) { - void __iomem *port_mmio = ahci_port_base(ap); - u32 fbs = readl(port_mmio + PORT_FBS); - int pmp = fbs >> PORT_FBS_DWE_OFFSET; - - if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links) && - ata_link_online(&ap->pmp_link[pmp])) { - link = &ap->pmp_link[pmp]; - fbs_need_dec = true; - } - - } else - ata_for_each_link(link, ap, EDGE) - if (ata_link_active(link)) - break; - - if (!link) - link = &ap->link; - - active_qc = ata_qc_from_tag(ap, link->active_tag); - active_ehi = &link->eh_info; - - /* record irq stat */ - ata_ehi_clear_desc(host_ehi); - ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat); - - /* AHCI needs SError cleared; otherwise, it might lock up */ - ahci_scr_read(&ap->link, SCR_ERROR, &serror); - ahci_scr_write(&ap->link, SCR_ERROR, serror); - host_ehi->serror |= serror; - - /* some controllers set IRQ_IF_ERR on device errors, ignore it */ - if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR) - irq_stat &= ~PORT_IRQ_IF_ERR; - - if (irq_stat & PORT_IRQ_TF_ERR) { - /* If qc is active, charge it; otherwise, the active - * link. There's no active qc on NCQ errors. It will - * be determined by EH by reading log page 10h. - */ - if (active_qc) - active_qc->err_mask |= AC_ERR_DEV; - else - active_ehi->err_mask |= AC_ERR_DEV; - - if (hpriv->flags & AHCI_HFLAG_IGN_SERR_INTERNAL) - host_ehi->serror &= ~SERR_INTERNAL; - } - - if (irq_stat & PORT_IRQ_UNK_FIS) { - u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK); - - active_ehi->err_mask |= AC_ERR_HSM; - active_ehi->action |= ATA_EH_RESET; - ata_ehi_push_desc(active_ehi, - "unknown FIS %08x %08x %08x %08x" , - unk[0], unk[1], unk[2], unk[3]); - } - - if (sata_pmp_attached(ap) && (irq_stat & PORT_IRQ_BAD_PMP)) { - active_ehi->err_mask |= AC_ERR_HSM; - active_ehi->action |= ATA_EH_RESET; - ata_ehi_push_desc(active_ehi, "incorrect PMP"); - } - - if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) { - host_ehi->err_mask |= AC_ERR_HOST_BUS; - host_ehi->action |= ATA_EH_RESET; - ata_ehi_push_desc(host_ehi, "host bus error"); - } - - if (irq_stat & PORT_IRQ_IF_ERR) { - if (fbs_need_dec) - active_ehi->err_mask |= AC_ERR_DEV; - else { - host_ehi->err_mask |= AC_ERR_ATA_BUS; - host_ehi->action |= ATA_EH_RESET; - } - - ata_ehi_push_desc(host_ehi, "interface fatal error"); - } - - if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) { - ata_ehi_hotplugged(host_ehi); - ata_ehi_push_desc(host_ehi, "%s", - irq_stat & PORT_IRQ_CONNECT ? - "connection status changed" : "PHY RDY changed"); - } - - /* okay, let's hand over to EH */ - - if (irq_stat & PORT_IRQ_FREEZE) - ata_port_freeze(ap); - else if (fbs_need_dec) { - ata_link_abort(link); - ahci_fbs_dec_intr(ap); - } else - ata_port_abort(ap); -} - -static void ahci_port_intr(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - struct ata_eh_info *ehi = &ap->link.eh_info; - struct ahci_port_priv *pp = ap->private_data; - struct ahci_host_priv *hpriv = ap->host->private_data; - int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING); - u32 status, qc_active = 0; - int rc; - - status = readl(port_mmio + PORT_IRQ_STAT); - writel(status, port_mmio + PORT_IRQ_STAT); - - /* ignore BAD_PMP while resetting */ - if (unlikely(resetting)) - status &= ~PORT_IRQ_BAD_PMP; - - /* If we are getting PhyRdy, this is - * just a power state change, we should - * clear out this, plus the PhyRdy/Comm - * Wake bits from Serror - */ - if ((hpriv->flags & AHCI_HFLAG_NO_HOTPLUG) && - (status & PORT_IRQ_PHYRDY)) { - status &= ~PORT_IRQ_PHYRDY; - ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18))); - } - - if (unlikely(status & PORT_IRQ_ERROR)) { - ahci_error_intr(ap, status); - return; - } - - if (status & PORT_IRQ_SDB_FIS) { - /* If SNotification is available, leave notification - * handling to sata_async_notification(). If not, - * emulate it by snooping SDB FIS RX area. - * - * Snooping FIS RX area is probably cheaper than - * poking SNotification but some constrollers which - * implement SNotification, ICH9 for example, don't - * store AN SDB FIS into receive area. - */ - if (hpriv->cap & HOST_CAP_SNTF) - sata_async_notification(ap); - else { - /* If the 'N' bit in word 0 of the FIS is set, - * we just received asynchronous notification. - * Tell libata about it. - * - * Lack of SNotification should not appear in - * ahci 1.2, so the workaround is unnecessary - * when FBS is enabled. - */ - if (pp->fbs_enabled) - WARN_ON_ONCE(1); - else { - const __le32 *f = pp->rx_fis + RX_FIS_SDB; - u32 f0 = le32_to_cpu(f[0]); - if (f0 & (1 << 15)) - sata_async_notification(ap); - } - } - } - - /* pp->active_link is not reliable once FBS is enabled, both - * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because - * NCQ and non-NCQ commands may be in flight at the same time. - */ - if (pp->fbs_enabled) { - if (ap->qc_active) { - qc_active = readl(port_mmio + PORT_SCR_ACT); - qc_active |= readl(port_mmio + PORT_CMD_ISSUE); - } - } else { - /* pp->active_link is valid iff any command is in flight */ - if (ap->qc_active && pp->active_link->sactive) - qc_active = readl(port_mmio + PORT_SCR_ACT); - else - qc_active = readl(port_mmio + PORT_CMD_ISSUE); - } - - rc = ata_qc_complete_multiple(ap, qc_active); - - /* while resetting, invalid completions are expected */ - if (unlikely(rc < 0 && !resetting)) { - ehi->err_mask |= AC_ERR_HSM; - ehi->action |= ATA_EH_RESET; - ata_port_freeze(ap); - } -} - -static irqreturn_t ahci_interrupt(int irq, void *dev_instance) -{ - struct ata_host *host = dev_instance; - struct ahci_host_priv *hpriv; - unsigned int i, handled = 0; - void __iomem *mmio; - u32 irq_stat, irq_masked; - - VPRINTK("ENTER\n"); - - hpriv = host->private_data; - mmio = host->iomap[AHCI_PCI_BAR]; - - /* sigh. 0xffffffff is a valid return from h/w */ - irq_stat = readl(mmio + HOST_IRQ_STAT); - if (!irq_stat) - return IRQ_NONE; - - irq_masked = irq_stat & hpriv->port_map; - - spin_lock(&host->lock); - - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap; - - if (!(irq_masked & (1 << i))) - continue; - - ap = host->ports[i]; - if (ap) { - ahci_port_intr(ap); - VPRINTK("port %u\n", i); - } else { - VPRINTK("port %u (no irq)\n", i); - if (ata_ratelimit()) - dev_printk(KERN_WARNING, host->dev, - "interrupt on disabled port %u\n", i); - } - - handled = 1; - } - - /* HOST_IRQ_STAT behaves as level triggered latch meaning that - * it should be cleared after all the port events are cleared; - * otherwise, it will raise a spurious interrupt after each - * valid one. Please read section 10.6.2 of ahci 1.1 for more - * information. - * - * Also, use the unmasked value to clear interrupt as spurious - * pending event on a dummy port might cause screaming IRQ. - */ - writel(irq_stat, mmio + HOST_IRQ_STAT); - - spin_unlock(&host->lock); - - VPRINTK("EXIT\n"); - - return IRQ_RETVAL(handled); -} - -static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void __iomem *port_mmio = ahci_port_base(ap); - struct ahci_port_priv *pp = ap->private_data; - - /* Keep track of the currently active link. It will be used - * in completion path to determine whether NCQ phase is in - * progress. - */ - pp->active_link = qc->dev->link; - - if (qc->tf.protocol == ATA_PROT_NCQ) - writel(1 << qc->tag, port_mmio + PORT_SCR_ACT); - - if (pp->fbs_enabled && pp->fbs_last_dev != qc->dev->link->pmp) { - u32 fbs = readl(port_mmio + PORT_FBS); - fbs &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC); - fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET; - writel(fbs, port_mmio + PORT_FBS); - pp->fbs_last_dev = qc->dev->link->pmp; - } - - writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE); - - ahci_sw_activity(qc->dev->link); - - return 0; -} - -static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) -{ - struct ahci_port_priv *pp = qc->ap->private_data; - u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; - - if (pp->fbs_enabled) - d2h_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ; - - ata_tf_from_fis(d2h_fis, &qc->result_tf); - return true; -} - -static void ahci_freeze(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - - /* turn IRQ off */ - writel(0, port_mmio + PORT_IRQ_MASK); -} - -static void ahci_thaw(struct ata_port *ap) -{ - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(ap); - u32 tmp; - struct ahci_port_priv *pp = ap->private_data; - - /* clear IRQ */ - tmp = readl(port_mmio + PORT_IRQ_STAT); - writel(tmp, port_mmio + PORT_IRQ_STAT); - writel(1 << ap->port_no, mmio + HOST_IRQ_STAT); - - /* turn IRQ back on */ - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); -} - -static void ahci_error_handler(struct ata_port *ap) -{ - if (!(ap->pflags & ATA_PFLAG_FROZEN)) { - /* restart engine */ - ahci_stop_engine(ap); - ahci_start_engine(ap); - } - - sata_pmp_error_handler(ap); -} - -static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - - /* make DMA engine forget about the failed command */ - if (qc->flags & ATA_QCFLAG_FAILED) - ahci_kick_engine(ap); -} - -static void ahci_enable_fbs(struct ata_port *ap) -{ - struct ahci_port_priv *pp = ap->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 fbs; - int rc; - - if (!pp->fbs_supported) - return; - - fbs = readl(port_mmio + PORT_FBS); - if (fbs & PORT_FBS_EN) { - pp->fbs_enabled = true; - pp->fbs_last_dev = -1; /* initialization */ - return; - } - - rc = ahci_stop_engine(ap); - if (rc) - return; - - writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS); - fbs = readl(port_mmio + PORT_FBS); - if (fbs & PORT_FBS_EN) { - dev_printk(KERN_INFO, ap->host->dev, "FBS is enabled.\n"); - pp->fbs_enabled = true; - pp->fbs_last_dev = -1; /* initialization */ - } else - dev_printk(KERN_ERR, ap->host->dev, "Failed to enable FBS\n"); - - ahci_start_engine(ap); -} - -static void ahci_disable_fbs(struct ata_port *ap) -{ - struct ahci_port_priv *pp = ap->private_data; - void __iomem *port_mmio = ahci_port_base(ap); - u32 fbs; - int rc; - - if (!pp->fbs_supported) - return; - - fbs = readl(port_mmio + PORT_FBS); - if ((fbs & PORT_FBS_EN) == 0) { - pp->fbs_enabled = false; - return; - } - - rc = ahci_stop_engine(ap); - if (rc) - return; - - writel(fbs & ~PORT_FBS_EN, port_mmio + PORT_FBS); - fbs = readl(port_mmio + PORT_FBS); - if (fbs & PORT_FBS_EN) - dev_printk(KERN_ERR, ap->host->dev, "Failed to disable FBS\n"); - else { - dev_printk(KERN_INFO, ap->host->dev, "FBS is disabled.\n"); - pp->fbs_enabled = false; - } - - ahci_start_engine(ap); -} - -static void ahci_pmp_attach(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - struct ahci_port_priv *pp = ap->private_data; - u32 cmd; - - cmd = readl(port_mmio + PORT_CMD); - cmd |= PORT_CMD_PMP; - writel(cmd, port_mmio + PORT_CMD); - - ahci_enable_fbs(ap); - - pp->intr_mask |= PORT_IRQ_BAD_PMP; - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); -} - -static void ahci_pmp_detach(struct ata_port *ap) -{ - void __iomem *port_mmio = ahci_port_base(ap); - struct ahci_port_priv *pp = ap->private_data; - u32 cmd; - - ahci_disable_fbs(ap); - - cmd = readl(port_mmio + PORT_CMD); - cmd &= ~PORT_CMD_PMP; - writel(cmd, port_mmio + PORT_CMD); - - pp->intr_mask &= ~PORT_IRQ_BAD_PMP; - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); -} - -static int ahci_port_resume(struct ata_port *ap) -{ - ahci_power_up(ap); - ahci_start_port(ap); - - if (sata_pmp_attached(ap)) - ahci_pmp_attach(ap); - else - ahci_pmp_detach(ap); - - return 0; -} - #ifdef CONFIG_PM -static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) -{ - const char *emsg = NULL; - int rc; - - rc = ahci_deinit_port(ap, &emsg); - if (rc == 0) - ahci_power_down(ap); - else { - ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); - ahci_start_port(ap); - } - - return rc; -} - static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) { struct ata_host *host = dev_get_drvdata(&pdev->dev); struct ahci_host_priv *hpriv = host->private_data; - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; + void __iomem *mmio = hpriv->mmio; u32 ctl; if (mesg.event & PM_EVENT_SUSPEND && @@ -2675,11 +627,11 @@ static int ahci_pci_device_resume(struct pci_dev *pdev) return rc; if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { - rc = ahci_reset_controller(host); + rc = ahci_pci_reset_controller(host); if (rc) return rc; - ahci_init_controller(host); + ahci_pci_init_controller(host); } ata_host_resume(host); @@ -2688,92 +640,6 @@ static int ahci_pci_device_resume(struct pci_dev *pdev) } #endif -static int ahci_port_start(struct ata_port *ap) -{ - struct ahci_host_priv *hpriv = ap->host->private_data; - struct device *dev = ap->host->dev; - struct ahci_port_priv *pp; - void *mem; - dma_addr_t mem_dma; - size_t dma_sz, rx_fis_sz; - - pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); - if (!pp) - return -ENOMEM; - - /* check FBS capability */ - if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) { - void __iomem *port_mmio = ahci_port_base(ap); - u32 cmd = readl(port_mmio + PORT_CMD); - if (cmd & PORT_CMD_FBSCP) - pp->fbs_supported = true; - else - dev_printk(KERN_WARNING, dev, - "The port is not capable of FBS\n"); - } - - if (pp->fbs_supported) { - dma_sz = AHCI_PORT_PRIV_FBS_DMA_SZ; - rx_fis_sz = AHCI_RX_FIS_SZ * 16; - } else { - dma_sz = AHCI_PORT_PRIV_DMA_SZ; - rx_fis_sz = AHCI_RX_FIS_SZ; - } - - mem = dmam_alloc_coherent(dev, dma_sz, &mem_dma, GFP_KERNEL); - if (!mem) - return -ENOMEM; - memset(mem, 0, dma_sz); - - /* - * First item in chunk of DMA memory: 32-slot command table, - * 32 bytes each in size - */ - pp->cmd_slot = mem; - pp->cmd_slot_dma = mem_dma; - - mem += AHCI_CMD_SLOT_SZ; - mem_dma += AHCI_CMD_SLOT_SZ; - - /* - * Second item: Received-FIS area - */ - pp->rx_fis = mem; - pp->rx_fis_dma = mem_dma; - - mem += rx_fis_sz; - mem_dma += rx_fis_sz; - - /* - * Third item: data area for storing a single command - * and its scatter-gather table - */ - pp->cmd_tbl = mem; - pp->cmd_tbl_dma = mem_dma; - - /* - * Save off initial list of interrupts to be enabled. - * This could be changed later - */ - pp->intr_mask = DEF_PORT_IRQ; - - ap->private_data = pp; - - /* engage engines, captain */ - return ahci_port_resume(ap); -} - -static void ahci_port_stop(struct ata_port *ap) -{ - const char *emsg = NULL; - int rc; - - /* de-initialize port */ - rc = ahci_deinit_port(ap, &emsg); - if (rc) - ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); -} - static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) { int rc; @@ -2806,31 +672,12 @@ static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) return 0; } -static void ahci_print_info(struct ata_host *host) +static void ahci_pci_print_info(struct ata_host *host) { - struct ahci_host_priv *hpriv = host->private_data; struct pci_dev *pdev = to_pci_dev(host->dev); - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - u32 vers, cap, cap2, impl, speed; - const char *speed_s; u16 cc; const char *scc_s; - vers = readl(mmio + HOST_VERSION); - cap = hpriv->cap; - cap2 = hpriv->cap2; - impl = hpriv->port_map; - - speed = (cap >> 20) & 0xf; - if (speed == 1) - speed_s = "1.5"; - else if (speed == 2) - speed_s = "3"; - else if (speed == 3) - speed_s = "6"; - else - speed_s = "?"; - pci_read_config_word(pdev, 0x0a, &cc); if (cc == PCI_CLASS_STORAGE_IDE) scc_s = "IDE"; @@ -2841,50 +688,7 @@ static void ahci_print_info(struct ata_host *host) else scc_s = "unknown"; - dev_printk(KERN_INFO, &pdev->dev, - "AHCI %02x%02x.%02x%02x " - "%u slots %u ports %s Gbps 0x%x impl %s mode\n" - , - - (vers >> 24) & 0xff, - (vers >> 16) & 0xff, - (vers >> 8) & 0xff, - vers & 0xff, - - ((cap >> 8) & 0x1f) + 1, - (cap & 0x1f) + 1, - speed_s, - impl, - scc_s); - - dev_printk(KERN_INFO, &pdev->dev, - "flags: " - "%s%s%s%s%s%s%s" - "%s%s%s%s%s%s%s" - "%s%s%s%s%s%s\n" - , - - cap & HOST_CAP_64 ? "64bit " : "", - cap & HOST_CAP_NCQ ? "ncq " : "", - cap & HOST_CAP_SNTF ? "sntf " : "", - cap & HOST_CAP_MPS ? "ilck " : "", - cap & HOST_CAP_SSS ? "stag " : "", - cap & HOST_CAP_ALPM ? "pm " : "", - cap & HOST_CAP_LED ? "led " : "", - cap & HOST_CAP_CLO ? "clo " : "", - cap & HOST_CAP_ONLY ? "only " : "", - cap & HOST_CAP_PMP ? "pmp " : "", - cap & HOST_CAP_FBS ? "fbs " : "", - cap & HOST_CAP_PIO_MULTI ? "pio " : "", - cap & HOST_CAP_SSC ? "slum " : "", - cap & HOST_CAP_PART ? "part " : "", - cap & HOST_CAP_CCC ? "ccc " : "", - cap & HOST_CAP_EMS ? "ems " : "", - cap & HOST_CAP_SXS ? "sxs " : "", - cap2 & HOST_CAP2_APST ? "apst " : "", - cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "", - cap2 & HOST_CAP2_BOH ? "boh " : "" - ); + ahci_print_info(host, scc_s); } /* On ASUS P5W DH Deluxe, the second port of PCI device 00:1f.2 is @@ -3308,41 +1112,28 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) pci_intx(pdev, 1); + hpriv->mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; + /* save initial config */ - ahci_save_initial_config(pdev, hpriv); + ahci_pci_save_initial_config(pdev, hpriv); /* prepare host */ if (hpriv->cap & HOST_CAP_NCQ) { pi.flags |= ATA_FLAG_NCQ; - /* Auto-activate optimization is supposed to be supported on - all AHCI controllers indicating NCQ support, but it seems - to be broken at least on some NVIDIA MCP79 chipsets. - Until we get info on which NVIDIA chipsets don't have this - issue, if any, disable AA on all NVIDIA AHCIs. */ - if (pdev->vendor != PCI_VENDOR_ID_NVIDIA) + /* + * Auto-activate optimization is supposed to be + * supported on all AHCI controllers indicating NCQ + * capability, but it seems to be broken on some + * chipsets including NVIDIAs. + */ + if (!(hpriv->flags & AHCI_HFLAG_NO_FPDMA_AA)) pi.flags |= ATA_FLAG_FPDMA_AA; } if (hpriv->cap & HOST_CAP_PMP) pi.flags |= ATA_FLAG_PMP; - if (ahci_em_messages && (hpriv->cap & HOST_CAP_EMS)) { - u8 messages; - void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; - u32 em_loc = readl(mmio + HOST_EM_LOC); - u32 em_ctl = readl(mmio + HOST_EM_CTL); - - messages = (em_ctl & EM_CTRL_MSG_TYPE) >> 16; - - /* we only support LED message type right now */ - if ((messages & 0x01) && (ahci_em_messages == 1)) { - /* store em_loc */ - hpriv->em_loc = ((em_loc >> 16) * 4); - pi.flags |= ATA_FLAG_EM; - if (!(em_ctl & EM_CTL_ALHD)) - pi.flags |= ATA_FLAG_SW_ACTIVITY; - } - } + ahci_set_em_messages(hpriv, &pi); if (ahci_broken_system_poweroff(pdev)) { pi.flags |= ATA_FLAG_NO_POWEROFF_SPINDOWN; @@ -3372,7 +1163,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); if (!host) return -ENOMEM; - host->iomap = pcim_iomap_table(pdev); host->private_data = hpriv; if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) @@ -3395,7 +1185,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* set enclosure management message type */ if (ap->flags & ATA_FLAG_EM) - ap->em_message_type = ahci_em_messages; + ap->em_message_type = hpriv->em_msg_type; /* disabled/not-implemented port */ @@ -3414,12 +1204,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - rc = ahci_reset_controller(host); + rc = ahci_pci_reset_controller(host); if (rc) return rc; - ahci_init_controller(host); - ahci_print_info(host); + ahci_pci_init_controller(host); + ahci_pci_print_info(host); pci_set_master(pdev); return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h new file mode 100644 index 000000000000..7113c5724471 --- /dev/null +++ b/drivers/ata/ahci.h @@ -0,0 +1,343 @@ +/* + * ahci.h - Common AHCI SATA definitions and declarations + * + * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2004-2005 Red Hat, 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, 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. + * + * 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. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * AHCI hardware documentation: + * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf + * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf + * + */ + +#ifndef _AHCI_H +#define _AHCI_H + +#include <linux/libata.h> + +/* Enclosure Management Control */ +#define EM_CTRL_MSG_TYPE 0x000f0000 + +/* Enclosure Management LED Message Type */ +#define EM_MSG_LED_HBA_PORT 0x0000000f +#define EM_MSG_LED_PMP_SLOT 0x0000ff00 +#define EM_MSG_LED_VALUE 0xffff0000 +#define EM_MSG_LED_VALUE_ACTIVITY 0x00070000 +#define EM_MSG_LED_VALUE_OFF 0xfff80000 +#define EM_MSG_LED_VALUE_ON 0x00010000 + +enum { + AHCI_MAX_PORTS = 32, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, + AHCI_CMD_SZ = 32, + AHCI_CMD_SLOT_SZ = AHCI_MAX_CMDS * AHCI_CMD_SZ, + AHCI_RX_FIS_SZ = 256, + AHCI_CMD_TBL_CDB = 0x40, + AHCI_CMD_TBL_HDR_SZ = 0x80, + AHCI_CMD_TBL_SZ = AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16), + AHCI_CMD_TBL_AR_SZ = AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS, + AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + + AHCI_RX_FIS_SZ, + AHCI_PORT_PRIV_FBS_DMA_SZ = AHCI_CMD_SLOT_SZ + + AHCI_CMD_TBL_AR_SZ + + (AHCI_RX_FIS_SZ * 16), + AHCI_IRQ_ON_SG = (1 << 31), + AHCI_CMD_ATAPI = (1 << 5), + AHCI_CMD_WRITE = (1 << 6), + AHCI_CMD_PREFETCH = (1 << 7), + AHCI_CMD_RESET = (1 << 8), + AHCI_CMD_CLR_BUSY = (1 << 10), + + RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ + RX_FIS_SDB = 0x58, /* offset of SDB FIS data */ + RX_FIS_UNK = 0x60, /* offset of Unknown FIS data */ + + /* global controller registers */ + HOST_CAP = 0x00, /* host capabilities */ + HOST_CTL = 0x04, /* global host control */ + HOST_IRQ_STAT = 0x08, /* interrupt status */ + HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ + HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ + HOST_EM_LOC = 0x1c, /* Enclosure Management location */ + HOST_EM_CTL = 0x20, /* Enclosure Management Control */ + HOST_CAP2 = 0x24, /* host capabilities, extended */ + + /* HOST_CTL bits */ + HOST_RESET = (1 << 0), /* reset controller; self-clear */ + HOST_IRQ_EN = (1 << 1), /* global IRQ enable */ + HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ + + /* HOST_CAP bits */ + HOST_CAP_SXS = (1 << 5), /* Supports External SATA */ + HOST_CAP_EMS = (1 << 6), /* Enclosure Management support */ + HOST_CAP_CCC = (1 << 7), /* Command Completion Coalescing */ + HOST_CAP_PART = (1 << 13), /* Partial state capable */ + HOST_CAP_SSC = (1 << 14), /* Slumber state capable */ + HOST_CAP_PIO_MULTI = (1 << 15), /* PIO multiple DRQ support */ + HOST_CAP_FBS = (1 << 16), /* FIS-based switching support */ + HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ + HOST_CAP_ONLY = (1 << 18), /* Supports AHCI mode only */ + HOST_CAP_CLO = (1 << 24), /* Command List Override support */ + HOST_CAP_LED = (1 << 25), /* Supports activity LED */ + HOST_CAP_ALPM = (1 << 26), /* Aggressive Link PM support */ + HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ + HOST_CAP_MPS = (1 << 28), /* Mechanical presence switch */ + HOST_CAP_SNTF = (1 << 29), /* SNotification register */ + HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ + HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ + + /* HOST_CAP2 bits */ + HOST_CAP2_BOH = (1 << 0), /* BIOS/OS handoff supported */ + HOST_CAP2_NVMHCI = (1 << 1), /* NVMHCI supported */ + HOST_CAP2_APST = (1 << 2), /* Automatic partial to slumber */ + + /* registers for each SATA port */ + PORT_LST_ADDR = 0x00, /* command list DMA addr */ + PORT_LST_ADDR_HI = 0x04, /* command list DMA addr hi */ + PORT_FIS_ADDR = 0x08, /* FIS rx buf addr */ + PORT_FIS_ADDR_HI = 0x0c, /* FIS rx buf addr hi */ + PORT_IRQ_STAT = 0x10, /* interrupt status */ + PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */ + PORT_CMD = 0x18, /* port command */ + PORT_TFDATA = 0x20, /* taskfile data */ + PORT_SIG = 0x24, /* device TF signature */ + PORT_CMD_ISSUE = 0x38, /* command issue */ + PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */ + PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ + PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ + PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ + PORT_SCR_NTF = 0x3c, /* SATA phy register: SNotification */ + PORT_FBS = 0x40, /* FIS-based Switching */ + + /* PORT_IRQ_{STAT,MASK} bits */ + PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ + PORT_IRQ_TF_ERR = (1 << 30), /* task file error */ + PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */ + PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */ + PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */ + PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */ + PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */ + PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */ + + PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */ + PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */ + PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */ + PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */ + PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */ + PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */ + PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */ + PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */ + PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */ + + PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR | + PORT_IRQ_IF_ERR | + PORT_IRQ_CONNECT | + PORT_IRQ_PHYRDY | + PORT_IRQ_UNK_FIS | + PORT_IRQ_BAD_PMP, + PORT_IRQ_ERROR = PORT_IRQ_FREEZE | + PORT_IRQ_TF_ERR | + PORT_IRQ_HBUS_DATA_ERR, + DEF_PORT_IRQ = PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | + PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | + PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS, + + /* PORT_CMD bits */ + PORT_CMD_ASP = (1 << 27), /* Aggressive Slumber/Partial */ + PORT_CMD_ALPE = (1 << 26), /* Aggressive Link PM enable */ + PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */ + PORT_CMD_FBSCP = (1 << 22), /* FBS Capable Port */ + PORT_CMD_PMP = (1 << 17), /* PMP attached */ + PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ + PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */ + PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */ + PORT_CMD_CLO = (1 << 3), /* Command list override */ + PORT_CMD_POWER_ON = (1 << 2), /* Power up device */ + PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ + PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ + + PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */ + PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ + PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ + PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ + + PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */ + PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */ + PORT_FBS_DEV_OFFSET = 8, /* FBS device to issue offset */ + PORT_FBS_DEV_MASK = (0xf << PORT_FBS_DEV_OFFSET), /* FBS.DEV */ + PORT_FBS_SDE = (1 << 2), /* FBS single device error */ + PORT_FBS_DEC = (1 << 1), /* FBS device error clear */ + PORT_FBS_EN = (1 << 0), /* Enable FBS */ + + /* hpriv->flags bits */ + AHCI_HFLAG_NO_NCQ = (1 << 0), + AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */ + AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */ + AHCI_HFLAG_32BIT_ONLY = (1 << 3), /* force 32bit */ + AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */ + AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */ + AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */ + AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */ + AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */ + AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */ + AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */ + AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as + link offline */ + AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */ + AHCI_HFLAG_NO_FPDMA_AA = (1 << 13), /* no FPDMA AA */ + + /* ap->flags bits */ + + AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | + ATA_FLAG_ACPI_SATA | ATA_FLAG_AN | + ATA_FLAG_IPM, + + ICH_MAP = 0x90, /* ICH MAP register */ + + /* em constants */ + EM_MAX_SLOTS = 8, + EM_MAX_RETRY = 5, + + /* em_ctl bits */ + EM_CTL_RST = (1 << 9), /* Reset */ + EM_CTL_TM = (1 << 8), /* Transmit Message */ + EM_CTL_MR = (1 << 0), /* Message Recieved */ + EM_CTL_ALHD = (1 << 26), /* Activity LED */ + EM_CTL_XMT = (1 << 25), /* Transmit Only */ + EM_CTL_SMB = (1 << 24), /* Single Message Buffer */ + + /* em message type */ + EM_MSG_TYPE_LED = (1 << 0), /* LED */ + EM_MSG_TYPE_SAFTE = (1 << 1), /* SAF-TE */ + EM_MSG_TYPE_SES2 = (1 << 2), /* SES-2 */ + EM_MSG_TYPE_SGPIO = (1 << 3), /* SGPIO */ +}; + +struct ahci_cmd_hdr { + __le32 opts; + __le32 status; + __le32 tbl_addr; + __le32 tbl_addr_hi; + __le32 reserved[4]; +}; + +struct ahci_sg { + __le32 addr; + __le32 addr_hi; + __le32 reserved; + __le32 flags_size; +}; + +struct ahci_em_priv { + enum sw_activity blink_policy; + struct timer_list timer; + unsigned long saved_activity; + unsigned long activity; + unsigned long led_state; +}; + +struct ahci_port_priv { + struct ata_link *active_link; + struct ahci_cmd_hdr *cmd_slot; + dma_addr_t cmd_slot_dma; + void *cmd_tbl; + dma_addr_t cmd_tbl_dma; + void *rx_fis; + dma_addr_t rx_fis_dma; + /* for NCQ spurious interrupt analysis */ + unsigned int ncq_saw_d2h:1; + unsigned int ncq_saw_dmas:1; + unsigned int ncq_saw_sdb:1; + u32 intr_mask; /* interrupts to enable */ + bool fbs_supported; /* set iff FBS is supported */ + bool fbs_enabled; /* set iff FBS is enabled */ + int fbs_last_dev; /* save FBS.DEV of last FIS */ + /* enclosure management info per PM slot */ + struct ahci_em_priv em_priv[EM_MAX_SLOTS]; +}; + +struct ahci_host_priv { + void __iomem * mmio; /* bus-independant mem map */ + unsigned int flags; /* AHCI_HFLAG_* */ + u32 cap; /* cap to use */ + u32 cap2; /* cap2 to use */ + u32 port_map; /* port map to use */ + u32 saved_cap; /* saved initial cap */ + u32 saved_cap2; /* saved initial cap2 */ + u32 saved_port_map; /* saved initial port_map */ + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ +}; + +extern int ahci_ignore_sss; + +extern struct scsi_host_template ahci_sht; +extern struct ata_port_operations ahci_ops; + +void ahci_save_initial_config(struct device *dev, + struct ahci_host_priv *hpriv, + unsigned int force_port_map, + unsigned int mask_port_map); +void ahci_init_controller(struct ata_host *host); +int ahci_reset_controller(struct ata_host *host); + +int ahci_do_softreset(struct ata_link *link, unsigned int *class, + int pmp, unsigned long deadline, + int (*check_ready)(struct ata_link *link)); + +int ahci_stop_engine(struct ata_port *ap); +void ahci_start_engine(struct ata_port *ap); +int ahci_check_ready(struct ata_link *link); +int ahci_kick_engine(struct ata_port *ap); +void ahci_set_em_messages(struct ahci_host_priv *hpriv, + struct ata_port_info *pi); +int ahci_reset_em(struct ata_host *host); +irqreturn_t ahci_interrupt(int irq, void *dev_instance); +void ahci_print_info(struct ata_host *host, const char *scc_s); + +static inline void __iomem *__ahci_port_base(struct ata_host *host, + unsigned int port_no) +{ + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + + return mmio + 0x100 + (port_no * 0x80); +} + +static inline void __iomem *ahci_port_base(struct ata_port *ap) +{ + return __ahci_port_base(ap->host, ap->port_no); +} + +static inline int ahci_nr_ports(u32 cap) +{ + return (cap & 0x1f) + 1; +} + +#endif /* _AHCI_H */ diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c new file mode 100644 index 000000000000..5e11b160f247 --- /dev/null +++ b/drivers/ata/ahci_platform.c @@ -0,0 +1,192 @@ +/* + * AHCI SATA platform driver + * + * Copyright 2004-2005 Red Hat, Inc. + * Jeff Garzik <jgarzik@pobox.com> + * Copyright 2010 MontaVista Software, LLC. + * Anton Vorontsov <avorontsov@ru.mvista.com> + * + * 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, or (at your option) + * any later version. + */ + +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/ahci_platform.h> +#include "ahci.h" + +static int __init ahci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev->platform_data; + struct ata_port_info pi = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ahci_host_priv *hpriv; + struct ata_host *host; + struct resource *mem; + int irq; + int n_ports; + int i; + int rc; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(dev, "no mmio space\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "no irq\n"); + return -EINVAL; + } + + if (pdata && pdata->init) { + rc = pdata->init(dev); + if (rc) + return rc; + } + + if (pdata && pdata->ata_port_info) + pi = *pdata->ata_port_info; + + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + rc = -ENOMEM; + goto err0; + } + + hpriv->flags |= (unsigned long)pi.private_data; + + hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); + if (!hpriv->mmio) { + dev_err(dev, "can't map %pR\n", mem); + rc = -ENOMEM; + goto err0; + } + + ahci_save_initial_config(dev, hpriv, + pdata ? pdata->force_port_map : 0, + pdata ? pdata->mask_port_map : 0); + + /* prepare host */ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + + if (hpriv->cap & HOST_CAP_PMP) + pi.flags |= ATA_FLAG_PMP; + + ahci_set_em_messages(hpriv, &pi); + + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) { + rc = -ENOMEM; + goto err0; + } + + host->private_data = hpriv; + + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) + host->flags |= ATA_HOST_PARALLEL_SCAN; + else + printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n"); + + if (pi.flags & ATA_FLAG_EM) + ahci_reset_em(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + ata_port_desc(ap, "mmio %pR", mem); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set initial link pm policy */ + ap->pm_policy = NOT_AVAILABLE; + + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + } + + rc = ahci_reset_controller(host); + if (rc) + goto err0; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + + rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, + &ahci_sht); + if (rc) + goto err0; + + return 0; +err0: + if (pdata && pdata->exit) + pdata->exit(dev); + return rc; +} + +static int __devexit ahci_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev->platform_data; + struct ata_host *host = dev_get_drvdata(dev); + + ata_host_detach(host); + + if (pdata && pdata->exit) + pdata->exit(dev); + + return 0; +} + +static struct platform_driver ahci_driver = { + .probe = ahci_probe, + .remove = __devexit_p(ahci_remove), + .driver = { + .name = "ahci", + .owner = THIS_MODULE, + }, +}; + +static int __init ahci_init(void) +{ + return platform_driver_probe(&ahci_driver, ahci_probe); +} +module_init(ahci_init); + +static void __exit ahci_exit(void) +{ + platform_driver_unregister(&ahci_driver); +} +module_exit(ahci_exit); + +MODULE_DESCRIPTION("AHCI SATA platform driver"); +MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ahci"); diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 83bc49fac9bb..ec52fc618763 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -43,7 +43,7 @@ * driver the list of errata that are relevant is below, going back to * PIIX4. Older device documentation is now a bit tricky to find. * - * The chipsets all follow very much the same design. The orginal Triton + * The chipsets all follow very much the same design. The original Triton * series chipsets do _not_ support independant device timings, but this * is fixed in Triton II. With the odd mobile exception the chips then * change little except in gaining more modes until SATA arrives. This diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c new file mode 100644 index 000000000000..1984a6e89e84 --- /dev/null +++ b/drivers/ata/libahci.c @@ -0,0 +1,2216 @@ +/* + * libahci.c - Common AHCI SATA low-level routines + * + * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2004-2005 Red Hat, 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, 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. + * + * 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. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * AHCI hardware documentation: + * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf + * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf + * + */ + +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_cmnd.h> +#include <linux/libata.h> +#include "ahci.h" + +static int ahci_skip_host_reset; +int ahci_ignore_sss; +EXPORT_SYMBOL_GPL(ahci_ignore_sss); + +module_param_named(skip_host_reset, ahci_skip_host_reset, int, 0444); +MODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip)"); + +module_param_named(ignore_sss, ahci_ignore_sss, int, 0444); +MODULE_PARM_DESC(ignore_sss, "Ignore staggered spinup flag (0=don't ignore, 1=ignore)"); + +static int ahci_enable_alpm(struct ata_port *ap, + enum link_pm policy); +static void ahci_disable_alpm(struct ata_port *ap); +static ssize_t ahci_led_show(struct ata_port *ap, char *buf); +static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, + size_t size); +static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, + ssize_t size); + + + +static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); +static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); +static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc); +static int ahci_port_start(struct ata_port *ap); +static void ahci_port_stop(struct ata_port *ap); +static void ahci_qc_prep(struct ata_queued_cmd *qc); +static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc); +static void ahci_freeze(struct ata_port *ap); +static void ahci_thaw(struct ata_port *ap); +static void ahci_enable_fbs(struct ata_port *ap); +static void ahci_disable_fbs(struct ata_port *ap); +static void ahci_pmp_attach(struct ata_port *ap); +static void ahci_pmp_detach(struct ata_port *ap); +static int ahci_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline); +static int ahci_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline); +static void ahci_postreset(struct ata_link *link, unsigned int *class); +static void ahci_error_handler(struct ata_port *ap); +static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); +static int ahci_port_resume(struct ata_port *ap); +static void ahci_dev_config(struct ata_device *dev); +static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, + u32 opts); +#ifdef CONFIG_PM +static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); +#endif +static ssize_t ahci_activity_show(struct ata_device *dev, char *buf); +static ssize_t ahci_activity_store(struct ata_device *dev, + enum sw_activity val); +static void ahci_init_sw_activity(struct ata_link *link); + +static ssize_t ahci_show_host_caps(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t ahci_show_host_cap2(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t ahci_show_host_version(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t ahci_show_port_cmd(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t ahci_read_em_buffer(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t ahci_store_em_buffer(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size); + +static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL); +static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL); +static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL); +static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL); +static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO, + ahci_read_em_buffer, ahci_store_em_buffer); + +static struct device_attribute *ahci_shost_attrs[] = { + &dev_attr_link_power_management_policy, + &dev_attr_em_message_type, + &dev_attr_em_message, + &dev_attr_ahci_host_caps, + &dev_attr_ahci_host_cap2, + &dev_attr_ahci_host_version, + &dev_attr_ahci_port_cmd, + &dev_attr_em_buffer, + NULL +}; + +static struct device_attribute *ahci_sdev_attrs[] = { + &dev_attr_sw_activity, + &dev_attr_unload_heads, + NULL +}; + +struct scsi_host_template ahci_sht = { + ATA_NCQ_SHT("ahci"), + .can_queue = AHCI_MAX_CMDS - 1, + .sg_tablesize = AHCI_MAX_SG, + .dma_boundary = AHCI_DMA_BOUNDARY, + .shost_attrs = ahci_shost_attrs, + .sdev_attrs = ahci_sdev_attrs, +}; +EXPORT_SYMBOL_GPL(ahci_sht); + +struct ata_port_operations ahci_ops = { + .inherits = &sata_pmp_port_ops, + + .qc_defer = ahci_pmp_qc_defer, + .qc_prep = ahci_qc_prep, + .qc_issue = ahci_qc_issue, + .qc_fill_rtf = ahci_qc_fill_rtf, + + .freeze = ahci_freeze, + .thaw = ahci_thaw, + .softreset = ahci_softreset, + .hardreset = ahci_hardreset, + .postreset = ahci_postreset, + .pmp_softreset = ahci_softreset, + .error_handler = ahci_error_handler, + .post_internal_cmd = ahci_post_internal_cmd, + .dev_config = ahci_dev_config, + + .scr_read = ahci_scr_read, + .scr_write = ahci_scr_write, + .pmp_attach = ahci_pmp_attach, + .pmp_detach = ahci_pmp_detach, + + .enable_pm = ahci_enable_alpm, + .disable_pm = ahci_disable_alpm, + .em_show = ahci_led_show, + .em_store = ahci_led_store, + .sw_activity_show = ahci_activity_show, + .sw_activity_store = ahci_activity_store, +#ifdef CONFIG_PM + .port_suspend = ahci_port_suspend, + .port_resume = ahci_port_resume, +#endif + .port_start = ahci_port_start, + .port_stop = ahci_port_stop, +}; +EXPORT_SYMBOL_GPL(ahci_ops); + +int ahci_em_messages = 1; +EXPORT_SYMBOL_GPL(ahci_em_messages); +module_param(ahci_em_messages, int, 0444); +/* add other LED protocol types when they become supported */ +MODULE_PARM_DESC(ahci_em_messages, + "AHCI Enclosure Management Message control (0 = off, 1 = on)"); + +static void ahci_enable_ahci(void __iomem *mmio) +{ + int i; + u32 tmp; + + /* turn on AHCI_EN */ + tmp = readl(mmio + HOST_CTL); + if (tmp & HOST_AHCI_EN) + return; + + /* Some controllers need AHCI_EN to be written multiple times. + * Try a few times before giving up. + */ + for (i = 0; i < 5; i++) { + tmp |= HOST_AHCI_EN; + writel(tmp, mmio + HOST_CTL); + tmp = readl(mmio + HOST_CTL); /* flush && sanity check */ + if (tmp & HOST_AHCI_EN) + return; + msleep(10); + } + + WARN_ON(1); +} + +static ssize_t ahci_show_host_caps(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + struct ahci_host_priv *hpriv = ap->host->private_data; + + return sprintf(buf, "%x\n", hpriv->cap); +} + +static ssize_t ahci_show_host_cap2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + struct ahci_host_priv *hpriv = ap->host->private_data; + + return sprintf(buf, "%x\n", hpriv->cap2); +} + +static ssize_t ahci_show_host_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *mmio = hpriv->mmio; + + return sprintf(buf, "%x\n", readl(mmio + HOST_VERSION)); +} + +static ssize_t ahci_show_port_cmd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + void __iomem *port_mmio = ahci_port_base(ap); + + return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD)); +} + +static ssize_t ahci_read_em_buffer(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *mmio = hpriv->mmio; + void __iomem *em_mmio = mmio + hpriv->em_loc; + u32 em_ctl, msg; + unsigned long flags; + size_t count; + int i; + + spin_lock_irqsave(ap->lock, flags); + + em_ctl = readl(mmio + HOST_EM_CTL); + if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT || + !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) { + spin_unlock_irqrestore(ap->lock, flags); + return -EINVAL; + } + + if (!(em_ctl & EM_CTL_MR)) { + spin_unlock_irqrestore(ap->lock, flags); + return -EAGAIN; + } + + if (!(em_ctl & EM_CTL_SMB)) + em_mmio += hpriv->em_buf_sz; + + count = hpriv->em_buf_sz; + + /* the count should not be larger than PAGE_SIZE */ + if (count > PAGE_SIZE) { + if (printk_ratelimit()) + ata_port_printk(ap, KERN_WARNING, + "EM read buffer size too large: " + "buffer size %u, page size %lu\n", + hpriv->em_buf_sz, PAGE_SIZE); + count = PAGE_SIZE; + } + + for (i = 0; i < count; i += 4) { + msg = readl(em_mmio + i); + buf[i] = msg & 0xff; + buf[i + 1] = (msg >> 8) & 0xff; + buf[i + 2] = (msg >> 16) & 0xff; + buf[i + 3] = (msg >> 24) & 0xff; + } + + spin_unlock_irqrestore(ap->lock, flags); + + return i; +} + +static ssize_t ahci_store_em_buffer(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *mmio = hpriv->mmio; + void __iomem *em_mmio = mmio + hpriv->em_loc; + u32 em_ctl, msg; + unsigned long flags; + int i; + + /* check size validity */ + if (!(ap->flags & ATA_FLAG_EM) || + !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO) || + size % 4 || size > hpriv->em_buf_sz) + return -EINVAL; + + spin_lock_irqsave(ap->lock, flags); + + em_ctl = readl(mmio + HOST_EM_CTL); + if (em_ctl & EM_CTL_TM) { + spin_unlock_irqrestore(ap->lock, flags); + return -EBUSY; + } + + for (i = 0; i < size; i += 4) { + msg = buf[i] | buf[i + 1] << 8 | + buf[i + 2] << 16 | buf[i + 3] << 24; + writel(msg, em_mmio + i); + } + + writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); + + spin_unlock_irqrestore(ap->lock, flags); + + return size; +} + +/** + * ahci_save_initial_config - Save and fixup initial config values + * @dev: target AHCI device + * @hpriv: host private area to store config values + * @force_port_map: force port map to a specified value + * @mask_port_map: mask out particular bits from port map + * + * Some registers containing configuration info might be setup by + * BIOS and might be cleared on reset. This function saves the + * initial values of those registers into @hpriv such that they + * can be restored after controller reset. + * + * If inconsistent, config values are fixed up by this function. + * + * LOCKING: + * None. + */ +void ahci_save_initial_config(struct device *dev, + struct ahci_host_priv *hpriv, + unsigned int force_port_map, + unsigned int mask_port_map) +{ + void __iomem *mmio = hpriv->mmio; + u32 cap, cap2, vers, port_map; + int i; + + /* make sure AHCI mode is enabled before accessing CAP */ + ahci_enable_ahci(mmio); + + /* Values prefixed with saved_ are written back to host after + * reset. Values without are used for driver operation. + */ + hpriv->saved_cap = cap = readl(mmio + HOST_CAP); + hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); + + /* CAP2 register is only defined for AHCI 1.2 and later */ + vers = readl(mmio + HOST_VERSION); + if ((vers >> 16) > 1 || + ((vers >> 16) == 1 && (vers & 0xFFFF) >= 0x200)) + hpriv->saved_cap2 = cap2 = readl(mmio + HOST_CAP2); + else + hpriv->saved_cap2 = cap2 = 0; + + /* some chips have errata preventing 64bit use */ + if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) { + dev_printk(KERN_INFO, dev, + "controller can't do 64bit DMA, forcing 32bit\n"); + cap &= ~HOST_CAP_64; + } + + if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) { + dev_printk(KERN_INFO, dev, + "controller can't do NCQ, turning off CAP_NCQ\n"); + cap &= ~HOST_CAP_NCQ; + } + + if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) { + dev_printk(KERN_INFO, dev, + "controller can do NCQ, turning on CAP_NCQ\n"); + cap |= HOST_CAP_NCQ; + } + + if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) { + dev_printk(KERN_INFO, dev, + "controller can't do PMP, turning off CAP_PMP\n"); + cap &= ~HOST_CAP_PMP; + } + + if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) { + dev_printk(KERN_INFO, dev, + "controller can't do SNTF, turning off CAP_SNTF\n"); + cap &= ~HOST_CAP_SNTF; + } + + if (force_port_map && port_map != force_port_map) { + dev_printk(KERN_INFO, dev, "forcing port_map 0x%x -> 0x%x\n", + port_map, force_port_map); + port_map = force_port_map; + } + + if (mask_port_map) { + dev_printk(KERN_ERR, dev, "masking port_map 0x%x -> 0x%x\n", + port_map, + port_map & mask_port_map); + port_map &= mask_port_map; + } + + /* cross check port_map and cap.n_ports */ + if (port_map) { + int map_ports = 0; + + for (i = 0; i < AHCI_MAX_PORTS; i++) + if (port_map & (1 << i)) + map_ports++; + + /* If PI has more ports than n_ports, whine, clear + * port_map and let it be generated from n_ports. + */ + if (map_ports > ahci_nr_ports(cap)) { + dev_printk(KERN_WARNING, dev, + "implemented port map (0x%x) contains more " + "ports than nr_ports (%u), using nr_ports\n", + port_map, ahci_nr_ports(cap)); + port_map = 0; + } + } + + /* fabricate port_map from cap.nr_ports */ + if (!port_map) { + port_map = (1 << ahci_nr_ports(cap)) - 1; + dev_printk(KERN_WARNING, dev, + "forcing PORTS_IMPL to 0x%x\n", port_map); + + /* write the fixed up value to the PI register */ + hpriv->saved_port_map = port_map; + } + + /* record values to use during operation */ + hpriv->cap = cap; + hpriv->cap2 = cap2; + hpriv->port_map = port_map; +} +EXPORT_SYMBOL_GPL(ahci_save_initial_config); + +/** + * ahci_restore_initial_config - Restore initial config + * @host: target ATA host + * + * Restore initial config stored by ahci_save_initial_config(). + * + * LOCKING: + * None. + */ +static void ahci_restore_initial_config(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + + writel(hpriv->saved_cap, mmio + HOST_CAP); + if (hpriv->saved_cap2) + writel(hpriv->saved_cap2, mmio + HOST_CAP2); + writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL); + (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ +} + +static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg) +{ + static const int offset[] = { + [SCR_STATUS] = PORT_SCR_STAT, + [SCR_CONTROL] = PORT_SCR_CTL, + [SCR_ERROR] = PORT_SCR_ERR, + [SCR_ACTIVE] = PORT_SCR_ACT, + [SCR_NOTIFICATION] = PORT_SCR_NTF, + }; + struct ahci_host_priv *hpriv = ap->host->private_data; + + if (sc_reg < ARRAY_SIZE(offset) && + (sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF))) + return offset[sc_reg]; + return 0; +} + +static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) +{ + void __iomem *port_mmio = ahci_port_base(link->ap); + int offset = ahci_scr_offset(link->ap, sc_reg); + + if (offset) { + *val = readl(port_mmio + offset); + return 0; + } + return -EINVAL; +} + +static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) +{ + void __iomem *port_mmio = ahci_port_base(link->ap); + int offset = ahci_scr_offset(link->ap, sc_reg); + + if (offset) { + writel(val, port_mmio + offset); + return 0; + } + return -EINVAL; +} + +static int ahci_is_device_present(void __iomem *port_mmio) +{ + u8 status = readl(port_mmio + PORT_TFDATA) & 0xff; + + /* Make sure PxTFD.STS.BSY and PxTFD.STS.DRQ are 0 */ + if (status & (ATA_BUSY | ATA_DRQ)) + return 0; + + /* Make sure PxSSTS.DET is 3h */ + status = readl(port_mmio + PORT_SCR_STAT) & 0xf; + if (status != 3) + return 0; + return 1; +} + +void ahci_start_engine(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + u32 tmp; + + if (!ahci_is_device_present(port_mmio)) + return; + + /* start DMA */ + tmp = readl(port_mmio + PORT_CMD); + tmp |= PORT_CMD_START; + writel(tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ +} +EXPORT_SYMBOL_GPL(ahci_start_engine); + +int ahci_stop_engine(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + u32 tmp; + + tmp = readl(port_mmio + PORT_CMD); + + /* check if the HBA is idle */ + if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) + return 0; + + /* setting HBA to idle */ + tmp &= ~PORT_CMD_START; + writel(tmp, port_mmio + PORT_CMD); + + /* wait for engine to stop. This could be as long as 500 msec */ + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); + if (tmp & PORT_CMD_LIST_ON) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(ahci_stop_engine); + +static void ahci_start_fis_rx(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + u32 tmp; + + /* set FIS registers */ + if (hpriv->cap & HOST_CAP_64) + writel((pp->cmd_slot_dma >> 16) >> 16, + port_mmio + PORT_LST_ADDR_HI); + writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); + + if (hpriv->cap & HOST_CAP_64) + writel((pp->rx_fis_dma >> 16) >> 16, + port_mmio + PORT_FIS_ADDR_HI); + writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); + + /* enable FIS reception */ + tmp = readl(port_mmio + PORT_CMD); + tmp |= PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + + /* flush */ + readl(port_mmio + PORT_CMD); +} + +static int ahci_stop_fis_rx(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + u32 tmp; + + /* disable FIS reception */ + tmp = readl(port_mmio + PORT_CMD); + tmp &= ~PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + + /* wait for completion, spec says 500ms, give it 1000 */ + tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_FIS_ON, + PORT_CMD_FIS_ON, 10, 1000); + if (tmp & PORT_CMD_FIS_ON) + return -EBUSY; + + return 0; +} + +static void ahci_power_up(struct ata_port *ap) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 cmd; + + cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; + + /* spin up device */ + if (hpriv->cap & HOST_CAP_SSS) { + cmd |= PORT_CMD_SPIN_UP; + writel(cmd, port_mmio + PORT_CMD); + } + + /* wake up link */ + writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); +} + +static void ahci_disable_alpm(struct ata_port *ap) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 cmd; + struct ahci_port_priv *pp = ap->private_data; + + /* IPM bits should be disabled by libata-core */ + /* get the existing command bits */ + cmd = readl(port_mmio + PORT_CMD); + + /* disable ALPM and ASP */ + cmd &= ~PORT_CMD_ASP; + cmd &= ~PORT_CMD_ALPE; + + /* force the interface back to active */ + cmd |= PORT_CMD_ICC_ACTIVE; + + /* write out new cmd value */ + writel(cmd, port_mmio + PORT_CMD); + cmd = readl(port_mmio + PORT_CMD); + + /* wait 10ms to be sure we've come out of any low power state */ + msleep(10); + + /* clear out any PhyRdy stuff from interrupt status */ + writel(PORT_IRQ_PHYRDY, port_mmio + PORT_IRQ_STAT); + + /* go ahead and clean out PhyRdy Change from Serror too */ + ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18))); + + /* + * Clear flag to indicate that we should ignore all PhyRdy + * state changes + */ + hpriv->flags &= ~AHCI_HFLAG_NO_HOTPLUG; + + /* + * Enable interrupts on Phy Ready. + */ + pp->intr_mask |= PORT_IRQ_PHYRDY; + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); + + /* + * don't change the link pm policy - we can be called + * just to turn of link pm temporarily + */ +} + +static int ahci_enable_alpm(struct ata_port *ap, + enum link_pm policy) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 cmd; + struct ahci_port_priv *pp = ap->private_data; + u32 asp; + + /* Make sure the host is capable of link power management */ + if (!(hpriv->cap & HOST_CAP_ALPM)) + return -EINVAL; + + switch (policy) { + case MAX_PERFORMANCE: + case NOT_AVAILABLE: + /* + * if we came here with NOT_AVAILABLE, + * it just means this is the first time we + * have tried to enable - default to max performance, + * and let the user go to lower power modes on request. + */ + ahci_disable_alpm(ap); + return 0; + case MIN_POWER: + /* configure HBA to enter SLUMBER */ + asp = PORT_CMD_ASP; + break; + case MEDIUM_POWER: + /* configure HBA to enter PARTIAL */ + asp = 0; + break; + default: + return -EINVAL; + } + + /* + * Disable interrupts on Phy Ready. This keeps us from + * getting woken up due to spurious phy ready interrupts + * TBD - Hot plug should be done via polling now, is + * that even supported? + */ + pp->intr_mask &= ~PORT_IRQ_PHYRDY; + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); + + /* + * Set a flag to indicate that we should ignore all PhyRdy + * state changes since these can happen now whenever we + * change link state + */ + hpriv->flags |= AHCI_HFLAG_NO_HOTPLUG; + + /* get the existing command bits */ + cmd = readl(port_mmio + PORT_CMD); + + /* + * Set ASP based on Policy + */ + cmd |= asp; + + /* + * Setting this bit will instruct the HBA to aggressively + * enter a lower power link state when it's appropriate and + * based on the value set above for ASP + */ + cmd |= PORT_CMD_ALPE; + + /* write out new cmd value */ + writel(cmd, port_mmio + PORT_CMD); + cmd = readl(port_mmio + PORT_CMD); + + /* IPM bits should be set by libata-core */ + return 0; +} + +#ifdef CONFIG_PM +static void ahci_power_down(struct ata_port *ap) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 cmd, scontrol; + + if (!(hpriv->cap & HOST_CAP_SSS)) + return; + + /* put device into listen mode, first set PxSCTL.DET to 0 */ + scontrol = readl(port_mmio + PORT_SCR_CTL); + scontrol &= ~0xf; + writel(scontrol, port_mmio + PORT_SCR_CTL); + + /* then set PxCMD.SUD to 0 */ + cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; + cmd &= ~PORT_CMD_SPIN_UP; + writel(cmd, port_mmio + PORT_CMD); +} +#endif + +static void ahci_start_port(struct ata_port *ap) +{ + struct ahci_port_priv *pp = ap->private_data; + struct ata_link *link; + struct ahci_em_priv *emp; + ssize_t rc; + int i; + + /* enable FIS reception */ + ahci_start_fis_rx(ap); + + /* enable DMA */ + ahci_start_engine(ap); + + /* turn on LEDs */ + if (ap->flags & ATA_FLAG_EM) { + ata_for_each_link(link, ap, EDGE) { + emp = &pp->em_priv[link->pmp]; + + /* EM Transmit bit maybe busy during init */ + for (i = 0; i < EM_MAX_RETRY; i++) { + rc = ahci_transmit_led_message(ap, + emp->led_state, + 4); + if (rc == -EBUSY) + msleep(1); + else + break; + } + } + } + + if (ap->flags & ATA_FLAG_SW_ACTIVITY) + ata_for_each_link(link, ap, EDGE) + ahci_init_sw_activity(link); + +} + +static int ahci_deinit_port(struct ata_port *ap, const char **emsg) +{ + int rc; + + /* disable DMA */ + rc = ahci_stop_engine(ap); + if (rc) { + *emsg = "failed to stop engine"; + return rc; + } + + /* disable FIS reception */ + rc = ahci_stop_fis_rx(ap); + if (rc) { + *emsg = "failed stop FIS RX"; + return rc; + } + + return 0; +} + +int ahci_reset_controller(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 tmp; + + /* we must be in AHCI mode, before using anything + * AHCI-specific, such as HOST_RESET. + */ + ahci_enable_ahci(mmio); + + /* global controller reset */ + if (!ahci_skip_host_reset) { + tmp = readl(mmio + HOST_CTL); + if ((tmp & HOST_RESET) == 0) { + writel(tmp | HOST_RESET, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + } + + /* + * to perform host reset, OS should set HOST_RESET + * and poll until this bit is read to be "0". + * reset must complete within 1 second, or + * the hardware should be considered fried. + */ + tmp = ata_wait_register(mmio + HOST_CTL, HOST_RESET, + HOST_RESET, 10, 1000); + + if (tmp & HOST_RESET) { + dev_printk(KERN_ERR, host->dev, + "controller reset failed (0x%x)\n", tmp); + return -EIO; + } + + /* turn on AHCI mode */ + ahci_enable_ahci(mmio); + + /* Some registers might be cleared on reset. Restore + * initial values. + */ + ahci_restore_initial_config(host); + } else + dev_printk(KERN_INFO, host->dev, + "skipping global host reset\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(ahci_reset_controller); + +static void ahci_sw_activity(struct ata_link *link) +{ + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + + if (!(link->flags & ATA_LFLAG_SW_ACTIVITY)) + return; + + emp->activity++; + if (!timer_pending(&emp->timer)) + mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10)); +} + +static void ahci_sw_activity_blink(unsigned long arg) +{ + struct ata_link *link = (struct ata_link *)arg; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + unsigned long led_message = emp->led_state; + u32 activity_led_state; + unsigned long flags; + + led_message &= EM_MSG_LED_VALUE; + led_message |= ap->port_no | (link->pmp << 8); + + /* check to see if we've had activity. If so, + * toggle state of LED and reset timer. If not, + * turn LED to desired idle state. + */ + spin_lock_irqsave(ap->lock, flags); + if (emp->saved_activity != emp->activity) { + emp->saved_activity = emp->activity; + /* get the current LED state */ + activity_led_state = led_message & EM_MSG_LED_VALUE_ON; + + if (activity_led_state) + activity_led_state = 0; + else + activity_led_state = 1; + + /* clear old state */ + led_message &= ~EM_MSG_LED_VALUE_ACTIVITY; + + /* toggle state */ + led_message |= (activity_led_state << 16); + mod_timer(&emp->timer, jiffies + msecs_to_jiffies(100)); + } else { + /* switch to idle */ + led_message &= ~EM_MSG_LED_VALUE_ACTIVITY; + if (emp->blink_policy == BLINK_OFF) + led_message |= (1 << 16); + } + spin_unlock_irqrestore(ap->lock, flags); + ahci_transmit_led_message(ap, led_message, 4); +} + +static void ahci_init_sw_activity(struct ata_link *link) +{ + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + + /* init activity stats, setup timer */ + emp->saved_activity = emp->activity = 0; + setup_timer(&emp->timer, ahci_sw_activity_blink, (unsigned long)link); + + /* check our blink policy and set flag for link if it's enabled */ + if (emp->blink_policy) + link->flags |= ATA_LFLAG_SW_ACTIVITY; +} + +int ahci_reset_em(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 em_ctl; + + em_ctl = readl(mmio + HOST_EM_CTL); + if ((em_ctl & EM_CTL_TM) || (em_ctl & EM_CTL_RST)) + return -EINVAL; + + writel(em_ctl | EM_CTL_RST, mmio + HOST_EM_CTL); + return 0; +} +EXPORT_SYMBOL_GPL(ahci_reset_em); + +static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, + ssize_t size) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *mmio = hpriv->mmio; + u32 em_ctl; + u32 message[] = {0, 0}; + unsigned long flags; + int pmp; + struct ahci_em_priv *emp; + + /* get the slot number from the message */ + pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; + if (pmp < EM_MAX_SLOTS) + emp = &pp->em_priv[pmp]; + else + return -EINVAL; + + spin_lock_irqsave(ap->lock, flags); + + /* + * if we are still busy transmitting a previous message, + * do not allow + */ + em_ctl = readl(mmio + HOST_EM_CTL); + if (em_ctl & EM_CTL_TM) { + spin_unlock_irqrestore(ap->lock, flags); + return -EBUSY; + } + + if (hpriv->em_msg_type & EM_MSG_TYPE_LED) { + /* + * create message header - this is all zero except for + * the message size, which is 4 bytes. + */ + message[0] |= (4 << 8); + + /* ignore 0:4 of byte zero, fill in port info yourself */ + message[1] = ((state & ~EM_MSG_LED_HBA_PORT) | ap->port_no); + + /* write message to EM_LOC */ + writel(message[0], mmio + hpriv->em_loc); + writel(message[1], mmio + hpriv->em_loc+4); + + /* + * tell hardware to transmit the message + */ + writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); + } + + /* save off new led state for port/slot */ + emp->led_state = state; + + spin_unlock_irqrestore(ap->lock, flags); + return size; +} + +static ssize_t ahci_led_show(struct ata_port *ap, char *buf) +{ + struct ahci_port_priv *pp = ap->private_data; + struct ata_link *link; + struct ahci_em_priv *emp; + int rc = 0; + + ata_for_each_link(link, ap, EDGE) { + emp = &pp->em_priv[link->pmp]; + rc += sprintf(buf, "%lx\n", emp->led_state); + } + return rc; +} + +static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, + size_t size) +{ + int state; + int pmp; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp; + + state = simple_strtoul(buf, NULL, 0); + + /* get the slot number from the message */ + pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; + if (pmp < EM_MAX_SLOTS) + emp = &pp->em_priv[pmp]; + else + return -EINVAL; + + /* mask off the activity bits if we are in sw_activity + * mode, user should turn off sw_activity before setting + * activity led through em_message + */ + if (emp->blink_policy) + state &= ~EM_MSG_LED_VALUE_ACTIVITY; + + return ahci_transmit_led_message(ap, state, size); +} + +static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) +{ + struct ata_link *link = dev->link; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + u32 port_led_state = emp->led_state; + + /* save the desired Activity LED behavior */ + if (val == OFF) { + /* clear LFLAG */ + link->flags &= ~(ATA_LFLAG_SW_ACTIVITY); + + /* set the LED to OFF */ + port_led_state &= EM_MSG_LED_VALUE_OFF; + port_led_state |= (ap->port_no | (link->pmp << 8)); + ahci_transmit_led_message(ap, port_led_state, 4); + } else { + link->flags |= ATA_LFLAG_SW_ACTIVITY; + if (val == BLINK_OFF) { + /* set LED to ON for idle */ + port_led_state &= EM_MSG_LED_VALUE_OFF; + port_led_state |= (ap->port_no | (link->pmp << 8)); + port_led_state |= EM_MSG_LED_VALUE_ON; /* check this */ + ahci_transmit_led_message(ap, port_led_state, 4); + } + } + emp->blink_policy = val; + return 0; +} + +static ssize_t ahci_activity_show(struct ata_device *dev, char *buf) +{ + struct ata_link *link = dev->link; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; + + /* display the saved value of activity behavior for this + * disk. + */ + return sprintf(buf, "%d\n", emp->blink_policy); +} + +static void ahci_port_init(struct device *dev, struct ata_port *ap, + int port_no, void __iomem *mmio, + void __iomem *port_mmio) +{ + const char *emsg = NULL; + int rc; + u32 tmp; + + /* make sure port is not active */ + rc = ahci_deinit_port(ap, &emsg); + if (rc) + dev_warn(dev, "%s (%d)\n", emsg, rc); + + /* clear SError */ + tmp = readl(port_mmio + PORT_SCR_ERR); + VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* clear port IRQ */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << port_no, mmio + HOST_IRQ_STAT); +} + +void ahci_init_controller(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + int i; + void __iomem *port_mmio; + u32 tmp; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + port_mmio = ahci_port_base(ap); + if (ata_port_is_dummy(ap)) + continue; + + ahci_port_init(host->dev, ap, i, mmio, port_mmio); + } + + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); + writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); +} +EXPORT_SYMBOL_GPL(ahci_init_controller); + +static void ahci_dev_config(struct ata_device *dev) +{ + struct ahci_host_priv *hpriv = dev->link->ap->host->private_data; + + if (hpriv->flags & AHCI_HFLAG_SECT255) { + dev->max_sectors = 255; + ata_dev_printk(dev, KERN_INFO, + "SB600 AHCI: limiting to 255 sectors per cmd\n"); + } +} + +static unsigned int ahci_dev_classify(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_taskfile tf; + u32 tmp; + + tmp = readl(port_mmio + PORT_SIG); + tf.lbah = (tmp >> 24) & 0xff; + tf.lbam = (tmp >> 16) & 0xff; + tf.lbal = (tmp >> 8) & 0xff; + tf.nsect = (tmp) & 0xff; + + return ata_dev_classify(&tf); +} + +static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, + u32 opts) +{ + dma_addr_t cmd_tbl_dma; + + cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ; + + pp->cmd_slot[tag].opts = cpu_to_le32(opts); + pp->cmd_slot[tag].status = 0; + pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff); + pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16); +} + +int ahci_kick_engine(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_host_priv *hpriv = ap->host->private_data; + u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF; + u32 tmp; + int busy, rc; + + /* stop engine */ + rc = ahci_stop_engine(ap); + if (rc) + goto out_restart; + + /* need to do CLO? + * always do CLO if PMP is attached (AHCI-1.3 9.2) + */ + busy = status & (ATA_BUSY | ATA_DRQ); + if (!busy && !sata_pmp_attached(ap)) { + rc = 0; + goto out_restart; + } + + if (!(hpriv->cap & HOST_CAP_CLO)) { + rc = -EOPNOTSUPP; + goto out_restart; + } + + /* perform CLO */ + tmp = readl(port_mmio + PORT_CMD); + tmp |= PORT_CMD_CLO; + writel(tmp, port_mmio + PORT_CMD); + + rc = 0; + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); + if (tmp & PORT_CMD_CLO) + rc = -EIO; + + /* restart engine */ + out_restart: + ahci_start_engine(ap); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_kick_engine); + +static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, + struct ata_taskfile *tf, int is_cmd, u16 flags, + unsigned long timeout_msec) +{ + const u32 cmd_fis_len = 5; /* five dwords */ + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u8 *fis = pp->cmd_tbl; + u32 tmp; + + /* prep the command */ + ata_tf_to_fis(tf, pmp, is_cmd, fis); + ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12)); + + /* issue & wait */ + writel(1, port_mmio + PORT_CMD_ISSUE); + + if (timeout_msec) { + tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, + 1, timeout_msec); + if (tmp & 0x1) { + ahci_kick_engine(ap); + return -EBUSY; + } + } else + readl(port_mmio + PORT_CMD_ISSUE); /* flush */ + + return 0; +} + +int ahci_do_softreset(struct ata_link *link, unsigned int *class, + int pmp, unsigned long deadline, + int (*check_ready)(struct ata_link *link)) +{ + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + const char *reason = NULL; + unsigned long now, msecs; + struct ata_taskfile tf; + int rc; + + DPRINTK("ENTER\n"); + + /* prepare for SRST (AHCI-1.1 10.4.1) */ + rc = ahci_kick_engine(ap); + if (rc && rc != -EOPNOTSUPP) + ata_link_printk(link, KERN_WARNING, + "failed to reset engine (errno=%d)\n", rc); + + ata_tf_init(link->device, &tf); + + /* issue the first D2H Register FIS */ + msecs = 0; + now = jiffies; + if (time_after(now, deadline)) + msecs = jiffies_to_msecs(deadline - now); + + tf.ctl |= ATA_SRST; + if (ahci_exec_polled_cmd(ap, pmp, &tf, 0, + AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) { + rc = -EIO; + reason = "1st FIS failed"; + goto fail; + } + + /* spec says at least 5us, but be generous and sleep for 1ms */ + msleep(1); + + /* issue the second D2H Register FIS */ + tf.ctl &= ~ATA_SRST; + ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); + + /* wait for link to become ready */ + rc = ata_wait_after_reset(link, deadline, check_ready); + if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) { + /* + * Workaround for cases where link online status can't + * be trusted. Treat device readiness timeout as link + * offline. + */ + ata_link_printk(link, KERN_INFO, + "device not ready, treating as offline\n"); + *class = ATA_DEV_NONE; + } else if (rc) { + /* link occupied, -ENODEV too is an error */ + reason = "device not ready"; + goto fail; + } else + *class = ahci_dev_classify(ap); + + DPRINTK("EXIT, class=%u\n", *class); + return 0; + + fail: + ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason); + return rc; +} + +int ahci_check_ready(struct ata_link *link) +{ + void __iomem *port_mmio = ahci_port_base(link->ap); + u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF; + + return ata_check_ready(status); +} +EXPORT_SYMBOL_GPL(ahci_check_ready); + +static int ahci_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int pmp = sata_srst_pmp(link); + + DPRINTK("ENTER\n"); + + return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); +} +EXPORT_SYMBOL_GPL(ahci_do_softreset); + +static int ahci_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; + int rc; + + DPRINTK("ENTER\n"); + + ahci_stop_engine(ap); + + /* clear D2H reception area to properly wait for D2H FIS */ + ata_tf_init(link->device, &tf); + tf.command = 0x80; + ata_tf_to_fis(&tf, 0, 0, d2h_fis); + + rc = sata_link_hardreset(link, timing, deadline, &online, + ahci_check_ready); + + ahci_start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); + + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); + return rc; +} + +static void ahci_postreset(struct ata_link *link, unsigned int *class) +{ + struct ata_port *ap = link->ap; + void __iomem *port_mmio = ahci_port_base(ap); + u32 new_tmp, tmp; + + ata_std_postreset(link, class); + + /* Make sure port's ATAPI bit is set appropriately */ + new_tmp = tmp = readl(port_mmio + PORT_CMD); + if (*class == ATA_DEV_ATAPI) + new_tmp |= PORT_CMD_ATAPI; + else + new_tmp &= ~PORT_CMD_ATAPI; + if (new_tmp != tmp) { + writel(new_tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + } +} + +static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl) +{ + struct scatterlist *sg; + struct ahci_sg *ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ; + unsigned int si; + + VPRINTK("ENTER\n"); + + /* + * Next, the S/G list. + */ + for_each_sg(qc->sg, sg, qc->n_elem, si) { + dma_addr_t addr = sg_dma_address(sg); + u32 sg_len = sg_dma_len(sg); + + ahci_sg[si].addr = cpu_to_le32(addr & 0xffffffff); + ahci_sg[si].addr_hi = cpu_to_le32((addr >> 16) >> 16); + ahci_sg[si].flags_size = cpu_to_le32(sg_len - 1); + } + + return si; +} + +static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ahci_port_priv *pp = ap->private_data; + + if (!sata_pmp_attached(ap) || pp->fbs_enabled) + return ata_std_qc_defer(qc); + else + return sata_pmp_qc_defer_cmd_switch(qc); +} + +static void ahci_qc_prep(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ahci_port_priv *pp = ap->private_data; + int is_atapi = ata_is_atapi(qc->tf.protocol); + void *cmd_tbl; + u32 opts; + const u32 cmd_fis_len = 5; /* five dwords */ + unsigned int n_elem; + + /* + * Fill in command table information. First, the header, + * a SATA Register - Host to Device command FIS. + */ + cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ; + + ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, cmd_tbl); + if (is_atapi) { + memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32); + memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len); + } + + n_elem = 0; + if (qc->flags & ATA_QCFLAG_DMAMAP) + n_elem = ahci_fill_sg(qc, cmd_tbl); + + /* + * Fill in command slot information. + */ + opts = cmd_fis_len | n_elem << 16 | (qc->dev->link->pmp << 12); + if (qc->tf.flags & ATA_TFLAG_WRITE) + opts |= AHCI_CMD_WRITE; + if (is_atapi) + opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH; + + ahci_fill_cmd_slot(pp, qc->tag, opts); +} + +static void ahci_fbs_dec_intr(struct ata_port *ap) +{ + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs = readl(port_mmio + PORT_FBS); + int retries = 3; + + DPRINTK("ENTER\n"); + BUG_ON(!pp->fbs_enabled); + + /* time to wait for DEC is not specified by AHCI spec, + * add a retry loop for safety. + */ + writel(fbs | PORT_FBS_DEC, port_mmio + PORT_FBS); + fbs = readl(port_mmio + PORT_FBS); + while ((fbs & PORT_FBS_DEC) && retries--) { + udelay(1); + fbs = readl(port_mmio + PORT_FBS); + } + + if (fbs & PORT_FBS_DEC) + dev_printk(KERN_ERR, ap->host->dev, + "failed to clear device error\n"); +} + +static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + struct ata_eh_info *host_ehi = &ap->link.eh_info; + struct ata_link *link = NULL; + struct ata_queued_cmd *active_qc; + struct ata_eh_info *active_ehi; + bool fbs_need_dec = false; + u32 serror; + + /* determine active link with error */ + if (pp->fbs_enabled) { + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs = readl(port_mmio + PORT_FBS); + int pmp = fbs >> PORT_FBS_DWE_OFFSET; + + if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links) && + ata_link_online(&ap->pmp_link[pmp])) { + link = &ap->pmp_link[pmp]; + fbs_need_dec = true; + } + + } else + ata_for_each_link(link, ap, EDGE) + if (ata_link_active(link)) + break; + + if (!link) + link = &ap->link; + + active_qc = ata_qc_from_tag(ap, link->active_tag); + active_ehi = &link->eh_info; + + /* record irq stat */ + ata_ehi_clear_desc(host_ehi); + ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat); + + /* AHCI needs SError cleared; otherwise, it might lock up */ + ahci_scr_read(&ap->link, SCR_ERROR, &serror); + ahci_scr_write(&ap->link, SCR_ERROR, serror); + host_ehi->serror |= serror; + + /* some controllers set IRQ_IF_ERR on device errors, ignore it */ + if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR) + irq_stat &= ~PORT_IRQ_IF_ERR; + + if (irq_stat & PORT_IRQ_TF_ERR) { + /* If qc is active, charge it; otherwise, the active + * link. There's no active qc on NCQ errors. It will + * be determined by EH by reading log page 10h. + */ + if (active_qc) + active_qc->err_mask |= AC_ERR_DEV; + else + active_ehi->err_mask |= AC_ERR_DEV; + + if (hpriv->flags & AHCI_HFLAG_IGN_SERR_INTERNAL) + host_ehi->serror &= ~SERR_INTERNAL; + } + + if (irq_stat & PORT_IRQ_UNK_FIS) { + u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK); + + active_ehi->err_mask |= AC_ERR_HSM; + active_ehi->action |= ATA_EH_RESET; + ata_ehi_push_desc(active_ehi, + "unknown FIS %08x %08x %08x %08x" , + unk[0], unk[1], unk[2], unk[3]); + } + + if (sata_pmp_attached(ap) && (irq_stat & PORT_IRQ_BAD_PMP)) { + active_ehi->err_mask |= AC_ERR_HSM; + active_ehi->action |= ATA_EH_RESET; + ata_ehi_push_desc(active_ehi, "incorrect PMP"); + } + + if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) { + host_ehi->err_mask |= AC_ERR_HOST_BUS; + host_ehi->action |= ATA_EH_RESET; + ata_ehi_push_desc(host_ehi, "host bus error"); + } + + if (irq_stat & PORT_IRQ_IF_ERR) { + if (fbs_need_dec) + active_ehi->err_mask |= AC_ERR_DEV; + else { + host_ehi->err_mask |= AC_ERR_ATA_BUS; + host_ehi->action |= ATA_EH_RESET; + } + + ata_ehi_push_desc(host_ehi, "interface fatal error"); + } + + if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) { + ata_ehi_hotplugged(host_ehi); + ata_ehi_push_desc(host_ehi, "%s", + irq_stat & PORT_IRQ_CONNECT ? + "connection status changed" : "PHY RDY changed"); + } + + /* okay, let's hand over to EH */ + + if (irq_stat & PORT_IRQ_FREEZE) + ata_port_freeze(ap); + else if (fbs_need_dec) { + ata_link_abort(link); + ahci_fbs_dec_intr(ap); + } else + ata_port_abort(ap); +} + +static void ahci_port_intr(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_eh_info *ehi = &ap->link.eh_info; + struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; + int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING); + u32 status, qc_active = 0; + int rc; + + status = readl(port_mmio + PORT_IRQ_STAT); + writel(status, port_mmio + PORT_IRQ_STAT); + + /* ignore BAD_PMP while resetting */ + if (unlikely(resetting)) + status &= ~PORT_IRQ_BAD_PMP; + + /* If we are getting PhyRdy, this is + * just a power state change, we should + * clear out this, plus the PhyRdy/Comm + * Wake bits from Serror + */ + if ((hpriv->flags & AHCI_HFLAG_NO_HOTPLUG) && + (status & PORT_IRQ_PHYRDY)) { + status &= ~PORT_IRQ_PHYRDY; + ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18))); + } + + if (unlikely(status & PORT_IRQ_ERROR)) { + ahci_error_intr(ap, status); + return; + } + + if (status & PORT_IRQ_SDB_FIS) { + /* If SNotification is available, leave notification + * handling to sata_async_notification(). If not, + * emulate it by snooping SDB FIS RX area. + * + * Snooping FIS RX area is probably cheaper than + * poking SNotification but some constrollers which + * implement SNotification, ICH9 for example, don't + * store AN SDB FIS into receive area. + */ + if (hpriv->cap & HOST_CAP_SNTF) + sata_async_notification(ap); + else { + /* If the 'N' bit in word 0 of the FIS is set, + * we just received asynchronous notification. + * Tell libata about it. + * + * Lack of SNotification should not appear in + * ahci 1.2, so the workaround is unnecessary + * when FBS is enabled. + */ + if (pp->fbs_enabled) + WARN_ON_ONCE(1); + else { + const __le32 *f = pp->rx_fis + RX_FIS_SDB; + u32 f0 = le32_to_cpu(f[0]); + if (f0 & (1 << 15)) + sata_async_notification(ap); + } + } + } + + /* pp->active_link is not reliable once FBS is enabled, both + * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because + * NCQ and non-NCQ commands may be in flight at the same time. + */ + if (pp->fbs_enabled) { + if (ap->qc_active) { + qc_active = readl(port_mmio + PORT_SCR_ACT); + qc_active |= readl(port_mmio + PORT_CMD_ISSUE); + } + } else { + /* pp->active_link is valid iff any command is in flight */ + if (ap->qc_active && pp->active_link->sactive) + qc_active = readl(port_mmio + PORT_SCR_ACT); + else + qc_active = readl(port_mmio + PORT_CMD_ISSUE); + } + + + rc = ata_qc_complete_multiple(ap, qc_active); + + /* while resetting, invalid completions are expected */ + if (unlikely(rc < 0 && !resetting)) { + ehi->err_mask |= AC_ERR_HSM; + ehi->action |= ATA_EH_RESET; + ata_port_freeze(ap); + } +} + +irqreturn_t ahci_interrupt(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct ahci_host_priv *hpriv; + unsigned int i, handled = 0; + void __iomem *mmio; + u32 irq_stat, irq_masked; + + VPRINTK("ENTER\n"); + + hpriv = host->private_data; + mmio = hpriv->mmio; + + /* sigh. 0xffffffff is a valid return from h/w */ + irq_stat = readl(mmio + HOST_IRQ_STAT); + if (!irq_stat) + return IRQ_NONE; + + irq_masked = irq_stat & hpriv->port_map; + + spin_lock(&host->lock); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap; + + if (!(irq_masked & (1 << i))) + continue; + + ap = host->ports[i]; + if (ap) { + ahci_port_intr(ap); + VPRINTK("port %u\n", i); + } else { + VPRINTK("port %u (no irq)\n", i); + if (ata_ratelimit()) + dev_printk(KERN_WARNING, host->dev, + "interrupt on disabled port %u\n", i); + } + + handled = 1; + } + + /* HOST_IRQ_STAT behaves as level triggered latch meaning that + * it should be cleared after all the port events are cleared; + * otherwise, it will raise a spurious interrupt after each + * valid one. Please read section 10.6.2 of ahci 1.1 for more + * information. + * + * Also, use the unmasked value to clear interrupt as spurious + * pending event on a dummy port might cause screaming IRQ. + */ + writel(irq_stat, mmio + HOST_IRQ_STAT); + + spin_unlock(&host->lock); + + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(handled); +} +EXPORT_SYMBOL_GPL(ahci_interrupt); + +static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_port_priv *pp = ap->private_data; + + /* Keep track of the currently active link. It will be used + * in completion path to determine whether NCQ phase is in + * progress. + */ + pp->active_link = qc->dev->link; + + if (qc->tf.protocol == ATA_PROT_NCQ) + writel(1 << qc->tag, port_mmio + PORT_SCR_ACT); + + if (pp->fbs_enabled && pp->fbs_last_dev != qc->dev->link->pmp) { + u32 fbs = readl(port_mmio + PORT_FBS); + fbs &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC); + fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET; + writel(fbs, port_mmio + PORT_FBS); + pp->fbs_last_dev = qc->dev->link->pmp; + } + + writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE); + + ahci_sw_activity(qc->dev->link); + + return 0; +} + +static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) +{ + struct ahci_port_priv *pp = qc->ap->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + + if (pp->fbs_enabled) + d2h_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ; + + ata_tf_from_fis(d2h_fis, &qc->result_tf); + return true; +} + +static void ahci_freeze(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + + /* turn IRQ off */ + writel(0, port_mmio + PORT_IRQ_MASK); +} + +static void ahci_thaw(struct ata_port *ap) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *mmio = hpriv->mmio; + void __iomem *port_mmio = ahci_port_base(ap); + u32 tmp; + struct ahci_port_priv *pp = ap->private_data; + + /* clear IRQ */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + writel(tmp, port_mmio + PORT_IRQ_STAT); + writel(1 << ap->port_no, mmio + HOST_IRQ_STAT); + + /* turn IRQ back on */ + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); +} + +static void ahci_error_handler(struct ata_port *ap) +{ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { + /* restart engine */ + ahci_stop_engine(ap); + ahci_start_engine(ap); + } + + sata_pmp_error_handler(ap); +} + +static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + + /* make DMA engine forget about the failed command */ + if (qc->flags & ATA_QCFLAG_FAILED) + ahci_kick_engine(ap); +} + +static void ahci_enable_fbs(struct ata_port *ap) +{ + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; + int rc; + + if (!pp->fbs_supported) + return; + + fbs = readl(port_mmio + PORT_FBS); + if (fbs & PORT_FBS_EN) { + pp->fbs_enabled = true; + pp->fbs_last_dev = -1; /* initialization */ + return; + } + + rc = ahci_stop_engine(ap); + if (rc) + return; + + writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS); + fbs = readl(port_mmio + PORT_FBS); + if (fbs & PORT_FBS_EN) { + dev_printk(KERN_INFO, ap->host->dev, "FBS is enabled.\n"); + pp->fbs_enabled = true; + pp->fbs_last_dev = -1; /* initialization */ + } else + dev_printk(KERN_ERR, ap->host->dev, "Failed to enable FBS\n"); + + ahci_start_engine(ap); +} + +static void ahci_disable_fbs(struct ata_port *ap) +{ + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; + int rc; + + if (!pp->fbs_supported) + return; + + fbs = readl(port_mmio + PORT_FBS); + if ((fbs & PORT_FBS_EN) == 0) { + pp->fbs_enabled = false; + return; + } + + rc = ahci_stop_engine(ap); + if (rc) + return; + + writel(fbs & ~PORT_FBS_EN, port_mmio + PORT_FBS); + fbs = readl(port_mmio + PORT_FBS); + if (fbs & PORT_FBS_EN) + dev_printk(KERN_ERR, ap->host->dev, "Failed to disable FBS\n"); + else { + dev_printk(KERN_INFO, ap->host->dev, "FBS is disabled.\n"); + pp->fbs_enabled = false; + } + + ahci_start_engine(ap); +} + +static void ahci_pmp_attach(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_port_priv *pp = ap->private_data; + u32 cmd; + + cmd = readl(port_mmio + PORT_CMD); + cmd |= PORT_CMD_PMP; + writel(cmd, port_mmio + PORT_CMD); + + ahci_enable_fbs(ap); + + pp->intr_mask |= PORT_IRQ_BAD_PMP; + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); +} + +static void ahci_pmp_detach(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_port_priv *pp = ap->private_data; + u32 cmd; + + ahci_disable_fbs(ap); + + cmd = readl(port_mmio + PORT_CMD); + cmd &= ~PORT_CMD_PMP; + writel(cmd, port_mmio + PORT_CMD); + + pp->intr_mask &= ~PORT_IRQ_BAD_PMP; + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); +} + +static int ahci_port_resume(struct ata_port *ap) +{ + ahci_power_up(ap); + ahci_start_port(ap); + + if (sata_pmp_attached(ap)) + ahci_pmp_attach(ap); + else + ahci_pmp_detach(ap); + + return 0; +} + +#ifdef CONFIG_PM +static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) +{ + const char *emsg = NULL; + int rc; + + rc = ahci_deinit_port(ap, &emsg); + if (rc == 0) + ahci_power_down(ap); + else { + ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); + ahci_start_port(ap); + } + + return rc; +} +#endif + +static int ahci_port_start(struct ata_port *ap) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + struct device *dev = ap->host->dev; + struct ahci_port_priv *pp; + void *mem; + dma_addr_t mem_dma; + size_t dma_sz, rx_fis_sz; + + pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + + /* check FBS capability */ + if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) { + void __iomem *port_mmio = ahci_port_base(ap); + u32 cmd = readl(port_mmio + PORT_CMD); + if (cmd & PORT_CMD_FBSCP) + pp->fbs_supported = true; + else + dev_printk(KERN_WARNING, dev, + "The port is not capable of FBS\n"); + } + + if (pp->fbs_supported) { + dma_sz = AHCI_PORT_PRIV_FBS_DMA_SZ; + rx_fis_sz = AHCI_RX_FIS_SZ * 16; + } else { + dma_sz = AHCI_PORT_PRIV_DMA_SZ; + rx_fis_sz = AHCI_RX_FIS_SZ; + } + + mem = dmam_alloc_coherent(dev, dma_sz, &mem_dma, GFP_KERNEL); + if (!mem) + return -ENOMEM; + memset(mem, 0, dma_sz); + + /* + * First item in chunk of DMA memory: 32-slot command table, + * 32 bytes each in size + */ + pp->cmd_slot = mem; + pp->cmd_slot_dma = mem_dma; + + mem += AHCI_CMD_SLOT_SZ; + mem_dma += AHCI_CMD_SLOT_SZ; + + /* + * Second item: Received-FIS area + */ + pp->rx_fis = mem; + pp->rx_fis_dma = mem_dma; + + mem += rx_fis_sz; + mem_dma += rx_fis_sz; + + /* + * Third item: data area for storing a single command + * and its scatter-gather table + */ + pp->cmd_tbl = mem; + pp->cmd_tbl_dma = mem_dma; + + /* + * Save off initial list of interrupts to be enabled. + * This could be changed later + */ + pp->intr_mask = DEF_PORT_IRQ; + + ap->private_data = pp; + + /* engage engines, captain */ + return ahci_port_resume(ap); +} + +static void ahci_port_stop(struct ata_port *ap) +{ + const char *emsg = NULL; + int rc; + + /* de-initialize port */ + rc = ahci_deinit_port(ap, &emsg); + if (rc) + ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); +} + +void ahci_print_info(struct ata_host *host, const char *scc_s) +{ + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 vers, cap, cap2, impl, speed; + const char *speed_s; + + vers = readl(mmio + HOST_VERSION); + cap = hpriv->cap; + cap2 = hpriv->cap2; + impl = hpriv->port_map; + + speed = (cap >> 20) & 0xf; + if (speed == 1) + speed_s = "1.5"; + else if (speed == 2) + speed_s = "3"; + else if (speed == 3) + speed_s = "6"; + else + speed_s = "?"; + + dev_info(host->dev, + "AHCI %02x%02x.%02x%02x " + "%u slots %u ports %s Gbps 0x%x impl %s mode\n" + , + + (vers >> 24) & 0xff, + (vers >> 16) & 0xff, + (vers >> 8) & 0xff, + vers & 0xff, + + ((cap >> 8) & 0x1f) + 1, + (cap & 0x1f) + 1, + speed_s, + impl, + scc_s); + + dev_info(host->dev, + "flags: " + "%s%s%s%s%s%s%s" + "%s%s%s%s%s%s%s" + "%s%s%s%s%s%s\n" + , + + cap & HOST_CAP_64 ? "64bit " : "", + cap & HOST_CAP_NCQ ? "ncq " : "", + cap & HOST_CAP_SNTF ? "sntf " : "", + cap & HOST_CAP_MPS ? "ilck " : "", + cap & HOST_CAP_SSS ? "stag " : "", + cap & HOST_CAP_ALPM ? "pm " : "", + cap & HOST_CAP_LED ? "led " : "", + cap & HOST_CAP_CLO ? "clo " : "", + cap & HOST_CAP_ONLY ? "only " : "", + cap & HOST_CAP_PMP ? "pmp " : "", + cap & HOST_CAP_FBS ? "fbs " : "", + cap & HOST_CAP_PIO_MULTI ? "pio " : "", + cap & HOST_CAP_SSC ? "slum " : "", + cap & HOST_CAP_PART ? "part " : "", + cap & HOST_CAP_CCC ? "ccc " : "", + cap & HOST_CAP_EMS ? "ems " : "", + cap & HOST_CAP_SXS ? "sxs " : "", + cap2 & HOST_CAP2_APST ? "apst " : "", + cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "", + cap2 & HOST_CAP2_BOH ? "boh " : "" + ); +} +EXPORT_SYMBOL_GPL(ahci_print_info); + +void ahci_set_em_messages(struct ahci_host_priv *hpriv, + struct ata_port_info *pi) +{ + u8 messages; + void __iomem *mmio = hpriv->mmio; + u32 em_loc = readl(mmio + HOST_EM_LOC); + u32 em_ctl = readl(mmio + HOST_EM_CTL); + + if (!ahci_em_messages || !(hpriv->cap & HOST_CAP_EMS)) + return; + + messages = (em_ctl & EM_CTRL_MSG_TYPE) >> 16; + + if (messages) { + /* store em_loc */ + hpriv->em_loc = ((em_loc >> 16) * 4); + hpriv->em_buf_sz = ((em_loc & 0xff) * 4); + hpriv->em_msg_type = messages; + pi->flags |= ATA_FLAG_EM; + if (!(em_ctl & EM_CTL_ALHD)) + pi->flags |= ATA_FLAG_SW_ACTIVITY; + } +} +EXPORT_SYMBOL_GPL(ahci_set_em_messages); + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("Common AHCI SATA low-level routines"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 49cffb6094a3..c47373f01f89 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -65,6 +65,7 @@ #include <linux/libata.h> #include <asm/byteorder.h> #include <linux/cdrom.h> +#include <linux/ratelimit.h> #include "libata.h" @@ -96,7 +97,6 @@ static void ata_dev_xfermask(struct ata_device *dev); static unsigned long ata_dev_blacklisted(const struct ata_device *dev); unsigned int ata_print_id = 1; -static struct workqueue_struct *ata_wq; struct workqueue_struct *ata_aux_wq; @@ -1685,52 +1685,6 @@ unsigned long ata_id_xfermask(const u16 *id) return ata_pack_xfermask(pio_mask, mwdma_mask, udma_mask); } -/** - * ata_pio_queue_task - Queue port_task - * @ap: The ata_port to queue port_task for - * @data: data for @fn to use - * @delay: delay time in msecs for workqueue function - * - * Schedule @fn(@data) for execution after @delay jiffies using - * port_task. There is one port_task per port and it's the - * user(low level driver)'s responsibility to make sure that only - * one task is active at any given time. - * - * libata core layer takes care of synchronization between - * port_task and EH. ata_pio_queue_task() may be ignored for EH - * synchronization. - * - * LOCKING: - * Inherited from caller. - */ -void ata_pio_queue_task(struct ata_port *ap, void *data, unsigned long delay) -{ - ap->port_task_data = data; - - /* may fail if ata_port_flush_task() in progress */ - queue_delayed_work(ata_wq, &ap->port_task, msecs_to_jiffies(delay)); -} - -/** - * ata_port_flush_task - Flush port_task - * @ap: The ata_port to flush port_task for - * - * After this function completes, port_task is guranteed not to - * be running or scheduled. - * - * LOCKING: - * Kernel thread context (may sleep) - */ -void ata_port_flush_task(struct ata_port *ap) -{ - DPRINTK("ENTER\n"); - - cancel_rearming_delayed_work(&ap->port_task); - - if (ata_msg_ctl(ap)) - ata_port_printk(ap, KERN_DEBUG, "%s: EXIT\n", __func__); -} - static void ata_qc_complete_internal(struct ata_queued_cmd *qc) { struct completion *waiting = qc->private_data; @@ -1852,7 +1806,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout)); - ata_port_flush_task(ap); + ata_sff_flush_pio_task(ap); if (!rc) { spin_lock_irqsave(ap->lock, flags); @@ -1906,22 +1860,6 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, ap->qc_active = preempted_qc_active; ap->nr_active_links = preempted_nr_active_links; - /* XXX - Some LLDDs (sata_mv) disable port on command failure. - * Until those drivers are fixed, we detect the condition - * here, fail the command with AC_ERR_SYSTEM and reenable the - * port. - * - * Note that this doesn't change any behavior as internal - * command failure results in disabling the device in the - * higher layer for LLDDs without new reset/EH callbacks. - * - * Kill the following code as soon as those drivers are fixed. - */ - if (ap->flags & ATA_FLAG_DISABLED) { - err_mask |= AC_ERR_SYSTEM; - ata_port_probe(ap); - } - spin_unlock_irqrestore(ap->lock, flags); if ((err_mask & AC_ERR_TIMEOUT) && auto_timeout) @@ -2767,8 +2705,6 @@ int ata_bus_probe(struct ata_port *ap) int rc; struct ata_device *dev; - ata_port_probe(ap); - ata_for_each_dev(dev, &ap->link, ALL) tries[dev->devno] = ATA_PROBE_MAX_TRIES; @@ -2796,8 +2732,7 @@ int ata_bus_probe(struct ata_port *ap) ap->ops->phy_reset(ap); ata_for_each_dev(dev, &ap->link, ALL) { - if (!(ap->flags & ATA_FLAG_DISABLED) && - dev->class != ATA_DEV_UNKNOWN) + if (dev->class != ATA_DEV_UNKNOWN) classes[dev->devno] = dev->class; else classes[dev->devno] = ATA_DEV_NONE; @@ -2805,8 +2740,6 @@ int ata_bus_probe(struct ata_port *ap) dev->class = ATA_DEV_UNKNOWN; } - ata_port_probe(ap); - /* read IDENTIFY page and configure devices. We have to do the identify specific sequence bass-ackwards so that PDIAG- is released by the slave device */ @@ -2856,8 +2789,6 @@ int ata_bus_probe(struct ata_port *ap) ata_for_each_dev(dev, &ap->link, ENABLED) return 0; - /* no device present, disable port */ - ata_port_disable(ap); return -ENODEV; fail: @@ -2889,22 +2820,6 @@ int ata_bus_probe(struct ata_port *ap) } /** - * ata_port_probe - Mark port as enabled - * @ap: Port for which we indicate enablement - * - * Modify @ap data structure such that the system - * thinks that the entire port is enabled. - * - * LOCKING: host lock, or some other form of - * serialization. - */ - -void ata_port_probe(struct ata_port *ap) -{ - ap->flags &= ~ATA_FLAG_DISABLED; -} - -/** * sata_print_link_status - Print SATA link status * @link: SATA link to printk link status about * @@ -2951,26 +2866,6 @@ struct ata_device *ata_dev_pair(struct ata_device *adev) } /** - * ata_port_disable - Disable port. - * @ap: Port to be disabled. - * - * Modify @ap data structure such that the system - * thinks that the entire port is disabled, and should - * never attempt to probe or communicate with devices - * on this port. - * - * LOCKING: host lock, or some other form of - * serialization. - */ - -void ata_port_disable(struct ata_port *ap) -{ - ap->link.device[0].class = ATA_DEV_NONE; - ap->link.device[1].class = ATA_DEV_NONE; - ap->flags |= ATA_FLAG_DISABLED; -} - -/** * sata_down_spd_limit - adjust SATA spd limit downward * @link: Link to adjust SATA spd limit for * @spd_limit: Additional limit @@ -3631,9 +3526,15 @@ int ata_wait_ready(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link)) { unsigned long start = jiffies; - unsigned long nodev_deadline = ata_deadline(start, ATA_TMOUT_FF_WAIT); + unsigned long nodev_deadline; int warned = 0; + /* choose which 0xff timeout to use, read comment in libata.h */ + if (link->ap->host->flags & ATA_HOST_PARALLEL_SCAN) + nodev_deadline = ata_deadline(start, ATA_TMOUT_FF_WAIT_LONG); + else + nodev_deadline = ata_deadline(start, ATA_TMOUT_FF_WAIT); + /* Slave readiness can't be tested separately from master. On * M/S emulation configuration, this function should be called * only on the master and it will handle both master and slave. @@ -3651,12 +3552,12 @@ int ata_wait_ready(struct ata_link *link, unsigned long deadline, if (ready > 0) return 0; - /* -ENODEV could be transient. Ignore -ENODEV if link + /* + * -ENODEV could be transient. Ignore -ENODEV if link * is online. Also, some SATA devices take a long - * time to clear 0xff after reset. For example, - * HHD424020F7SV00 iVDR needs >= 800ms while Quantum - * GoVault needs even more than that. Wait for - * ATA_TMOUT_FF_WAIT on -ENODEV if link isn't offline. + * time to clear 0xff after reset. Wait for + * ATA_TMOUT_FF_WAIT[_LONG] on -ENODEV if link isn't + * offline. * * Note that some PATA controllers (pata_ali) explode * if status register is read more than once when @@ -5558,30 +5459,6 @@ void ata_host_resume(struct ata_host *host) #endif /** - * ata_port_start - Set port up for dma. - * @ap: Port to initialize - * - * Called just after data structures for each port are - * initialized. Allocates space for PRD table. - * - * May be used as the port_start() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -int ata_port_start(struct ata_port *ap) -{ - struct device *dev = ap->dev; - - ap->prd = dmam_alloc_coherent(dev, ATA_PRD_TBL_SZ, &ap->prd_dma, - GFP_KERNEL); - if (!ap->prd) - return -ENOMEM; - - return 0; -} - -/** * ata_dev_init - Initialize an ata_device structure * @dev: Device structure to initialize * @@ -5709,12 +5586,9 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap->pflags |= ATA_PFLAG_INITIALIZING; ap->lock = &host->lock; - ap->flags = ATA_FLAG_DISABLED; ap->print_id = -1; - ap->ctl = ATA_DEVCTL_OBS; ap->host = host; ap->dev = host->dev; - ap->last_ctl = 0xFF; #if defined(ATA_VERBOSE_DEBUG) /* turn on all debugging levels */ @@ -5725,11 +5599,6 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN; #endif -#ifdef CONFIG_ATA_SFF - INIT_DELAYED_WORK(&ap->port_task, ata_pio_task); -#else - INIT_DELAYED_WORK(&ap->port_task, NULL); -#endif INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug); INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); INIT_LIST_HEAD(&ap->eh_done_q); @@ -5747,6 +5616,8 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap->stats.unhandled_irq = 1; ap->stats.idle_irq = 1; #endif + ata_sff_port_init(ap); + return ap; } @@ -6138,8 +6009,6 @@ static void async_port_probe(void *data, async_cookie_t cookie) struct ata_eh_info *ehi = &ap->link.eh_info; unsigned long flags; - ata_port_probe(ap); - /* kick EH for boot probing */ spin_lock_irqsave(ap->lock, flags); @@ -6663,62 +6532,43 @@ static void __init ata_parse_force_param(void) static int __init ata_init(void) { - ata_parse_force_param(); + int rc = -ENOMEM; - /* - * FIXME: In UP case, there is only one workqueue thread and if you - * have more than one PIO device, latency is bloody awful, with - * occasional multi-second "hiccups" as one PIO device waits for - * another. It's an ugly wart that users DO occasionally complain - * about; luckily most users have at most one PIO polled device. - */ - ata_wq = create_workqueue("ata"); - if (!ata_wq) - goto free_force_tbl; + ata_parse_force_param(); ata_aux_wq = create_singlethread_workqueue("ata_aux"); if (!ata_aux_wq) - goto free_wq; + goto fail; + + rc = ata_sff_init(); + if (rc) + goto fail; printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n"); return 0; -free_wq: - destroy_workqueue(ata_wq); -free_force_tbl: +fail: kfree(ata_force_tbl); - return -ENOMEM; + if (ata_aux_wq) + destroy_workqueue(ata_aux_wq); + return rc; } static void __exit ata_exit(void) { + ata_sff_exit(); kfree(ata_force_tbl); - destroy_workqueue(ata_wq); destroy_workqueue(ata_aux_wq); } subsys_initcall(ata_init); module_exit(ata_exit); -static unsigned long ratelimit_time; -static DEFINE_SPINLOCK(ata_ratelimit_lock); +static DEFINE_RATELIMIT_STATE(ratelimit, HZ / 5, 1); int ata_ratelimit(void) { - int rc; - unsigned long flags; - - spin_lock_irqsave(&ata_ratelimit_lock, flags); - - if (time_after(jiffies, ratelimit_time)) { - rc = 1; - ratelimit_time = jiffies + (HZ/5); - } else - rc = 0; - - spin_unlock_irqrestore(&ata_ratelimit_lock, flags); - - return rc; + return __ratelimit(&ratelimit); } /** @@ -6826,11 +6676,9 @@ EXPORT_SYMBOL_GPL(ata_xfer_mode2mask); EXPORT_SYMBOL_GPL(ata_xfer_mode2shift); EXPORT_SYMBOL_GPL(ata_mode_string); EXPORT_SYMBOL_GPL(ata_id_xfermask); -EXPORT_SYMBOL_GPL(ata_port_start); EXPORT_SYMBOL_GPL(ata_do_set_mode); EXPORT_SYMBOL_GPL(ata_std_qc_defer); EXPORT_SYMBOL_GPL(ata_noop_qc_prep); -EXPORT_SYMBOL_GPL(ata_port_probe); EXPORT_SYMBOL_GPL(ata_dev_disable); EXPORT_SYMBOL_GPL(sata_set_spd); EXPORT_SYMBOL_GPL(ata_wait_after_reset); @@ -6842,7 +6690,6 @@ EXPORT_SYMBOL_GPL(sata_std_hardreset); EXPORT_SYMBOL_GPL(ata_std_postreset); EXPORT_SYMBOL_GPL(ata_dev_classify); EXPORT_SYMBOL_GPL(ata_dev_pair); -EXPORT_SYMBOL_GPL(ata_port_disable); EXPORT_SYMBOL_GPL(ata_ratelimit); EXPORT_SYMBOL_GPL(ata_wait_register); EXPORT_SYMBOL_GPL(ata_scsi_queuecmd); @@ -6864,7 +6711,6 @@ EXPORT_SYMBOL_GPL(ata_id_c_string); EXPORT_SYMBOL_GPL(ata_do_dev_read_id); EXPORT_SYMBOL_GPL(ata_scsi_simulate); -EXPORT_SYMBOL_GPL(ata_pio_queue_task); EXPORT_SYMBOL_GPL(ata_pio_need_iordy); EXPORT_SYMBOL_GPL(ata_timing_find_mode); EXPORT_SYMBOL_GPL(ata_timing_compute); diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 228740f356c9..f77a67303f8b 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -550,8 +550,8 @@ void ata_scsi_error(struct Scsi_Host *host) DPRINTK("ENTER\n"); - /* synchronize with port task */ - ata_port_flush_task(ap); + /* make sure sff pio task is not running */ + ata_sff_flush_pio_task(ap); /* synchronize with host lock and sort out timeouts */ @@ -3684,7 +3684,7 @@ void ata_std_error_handler(struct ata_port *ap) ata_reset_fn_t hardreset = ops->hardreset; /* ignore built-in hardreset if SCR access is not available */ - if (ata_is_builtin_hardreset(hardreset) && !sata_scr_valid(&ap->link)) + if (hardreset == sata_std_hardreset && !sata_scr_valid(&ap->link)) hardreset = NULL; ata_do_eh(ap, ops->prereset, ops->softreset, hardreset, ops->postreset); diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 00305f41ed86..224faabd7b7e 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -231,10 +231,14 @@ static const char *sata_pmp_spec_rev_str(const u32 *gscr) return "<unknown>"; } +#define PMP_GSCR_SII_POL 129 + static int sata_pmp_configure(struct ata_device *dev, int print_info) { struct ata_port *ap = dev->link->ap; u32 *gscr = dev->gscr; + u16 vendor = sata_pmp_gscr_vendor(gscr); + u16 devid = sata_pmp_gscr_devid(gscr); unsigned int err_mask = 0; const char *reason; int nr_ports, rc; @@ -260,12 +264,34 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) goto fail; } + /* Disable sending Early R_OK. + * With "cached read" HDD testing and multiple ports busy on a SATA + * host controller, 3726 PMP will very rarely drop a deferred + * R_OK that was intended for the host. Symptom will be all + * 5 drives under test will timeout, get reset, and recover. + */ + if (vendor == 0x1095 && devid == 0x3726) { + u32 reg; + + err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, ®); + if (err_mask) { + rc = -EIO; + reason = "failed to read Sil3726 Private Register"; + goto fail; + } + reg &= ~0x1; + err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg); + if (err_mask) { + rc = -EIO; + reason = "failed to write Sil3726 Private Register"; + goto fail; + } + } + if (print_info) { ata_dev_printk(dev, KERN_INFO, "Port Multiplier %s, " "0x%04x:0x%04x r%d, %d ports, feat 0x%x/0x%x\n", - sata_pmp_spec_rev_str(gscr), - sata_pmp_gscr_vendor(gscr), - sata_pmp_gscr_devid(gscr), + sata_pmp_spec_rev_str(gscr), vendor, devid, sata_pmp_gscr_rev(gscr), nr_ports, gscr[SATA_PMP_GSCR_FEAT_EN], gscr[SATA_PMP_GSCR_FEAT]); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 0088cdeb0b1e..cfa9dd3d7253 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3345,9 +3345,6 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync) struct ata_link *link; struct ata_device *dev; - if (ap->flags & ATA_FLAG_DISABLED) - return; - repeat: ata_for_each_link(link, ap, EDGE) { ata_for_each_dev(dev, link, ENABLED) { diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index e3877b6843c9..19ddf924944f 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -40,10 +40,12 @@ #include "libata.h" +static struct workqueue_struct *ata_sff_wq; + const struct ata_port_operations ata_sff_port_ops = { .inherits = &ata_base_port_ops, - .qc_prep = ata_sff_qc_prep, + .qc_prep = ata_noop_qc_prep, .qc_issue = ata_sff_qc_issue, .qc_fill_rtf = ata_sff_qc_fill_rtf, @@ -53,9 +55,7 @@ const struct ata_port_operations ata_sff_port_ops = { .softreset = ata_sff_softreset, .hardreset = sata_sff_hardreset, .postreset = ata_sff_postreset, - .drain_fifo = ata_sff_drain_fifo, .error_handler = ata_sff_error_handler, - .post_internal_cmd = ata_sff_post_internal_cmd, .sff_dev_select = ata_sff_dev_select, .sff_check_status = ata_sff_check_status, @@ -63,178 +63,13 @@ const struct ata_port_operations ata_sff_port_ops = { .sff_tf_read = ata_sff_tf_read, .sff_exec_command = ata_sff_exec_command, .sff_data_xfer = ata_sff_data_xfer, - .sff_irq_on = ata_sff_irq_on, .sff_irq_clear = ata_sff_irq_clear, + .sff_drain_fifo = ata_sff_drain_fifo, .lost_interrupt = ata_sff_lost_interrupt, - - .port_start = ata_sff_port_start, }; EXPORT_SYMBOL_GPL(ata_sff_port_ops); -const struct ata_port_operations ata_bmdma_port_ops = { - .inherits = &ata_sff_port_ops, - - .mode_filter = ata_bmdma_mode_filter, - - .bmdma_setup = ata_bmdma_setup, - .bmdma_start = ata_bmdma_start, - .bmdma_stop = ata_bmdma_stop, - .bmdma_status = ata_bmdma_status, -}; -EXPORT_SYMBOL_GPL(ata_bmdma_port_ops); - -const struct ata_port_operations ata_bmdma32_port_ops = { - .inherits = &ata_bmdma_port_ops, - - .sff_data_xfer = ata_sff_data_xfer32, - .port_start = ata_sff_port_start32, -}; -EXPORT_SYMBOL_GPL(ata_bmdma32_port_ops); - -/** - * ata_fill_sg - Fill PCI IDE PRD table - * @qc: Metadata associated with taskfile to be transferred - * - * Fill PCI IDE PRD (scatter-gather) table with segments - * associated with the current disk command. - * - * LOCKING: - * spin_lock_irqsave(host lock) - * - */ -static void ata_fill_sg(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scatterlist *sg; - unsigned int si, pi; - - pi = 0; - for_each_sg(qc->sg, sg, qc->n_elem, si) { - u32 addr, offset; - u32 sg_len, len; - - /* determine if physical DMA addr spans 64K boundary. - * Note h/w doesn't support 64-bit, so we unconditionally - * truncate dma_addr_t to u32. - */ - addr = (u32) sg_dma_address(sg); - sg_len = sg_dma_len(sg); - - while (sg_len) { - offset = addr & 0xffff; - len = sg_len; - if ((offset + sg_len) > 0x10000) - len = 0x10000 - offset; - - ap->prd[pi].addr = cpu_to_le32(addr); - ap->prd[pi].flags_len = cpu_to_le32(len & 0xffff); - VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, len); - - pi++; - sg_len -= len; - addr += len; - } - } - - ap->prd[pi - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); -} - -/** - * ata_fill_sg_dumb - Fill PCI IDE PRD table - * @qc: Metadata associated with taskfile to be transferred - * - * Fill PCI IDE PRD (scatter-gather) table with segments - * associated with the current disk command. Perform the fill - * so that we avoid writing any length 64K records for - * controllers that don't follow the spec. - * - * LOCKING: - * spin_lock_irqsave(host lock) - * - */ -static void ata_fill_sg_dumb(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scatterlist *sg; - unsigned int si, pi; - - pi = 0; - for_each_sg(qc->sg, sg, qc->n_elem, si) { - u32 addr, offset; - u32 sg_len, len, blen; - - /* determine if physical DMA addr spans 64K boundary. - * Note h/w doesn't support 64-bit, so we unconditionally - * truncate dma_addr_t to u32. - */ - addr = (u32) sg_dma_address(sg); - sg_len = sg_dma_len(sg); - - while (sg_len) { - offset = addr & 0xffff; - len = sg_len; - if ((offset + sg_len) > 0x10000) - len = 0x10000 - offset; - - blen = len & 0xffff; - ap->prd[pi].addr = cpu_to_le32(addr); - if (blen == 0) { - /* Some PATA chipsets like the CS5530 can't - cope with 0x0000 meaning 64K as the spec - says */ - ap->prd[pi].flags_len = cpu_to_le32(0x8000); - blen = 0x8000; - ap->prd[++pi].addr = cpu_to_le32(addr + 0x8000); - } - ap->prd[pi].flags_len = cpu_to_le32(blen); - VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, len); - - pi++; - sg_len -= len; - addr += len; - } - } - - ap->prd[pi - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); -} - -/** - * ata_sff_qc_prep - Prepare taskfile for submission - * @qc: Metadata associated with taskfile to be prepared - * - * Prepare ATA taskfile for submission. - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ -void ata_sff_qc_prep(struct ata_queued_cmd *qc) -{ - if (!(qc->flags & ATA_QCFLAG_DMAMAP)) - return; - - ata_fill_sg(qc); -} -EXPORT_SYMBOL_GPL(ata_sff_qc_prep); - -/** - * ata_sff_dumb_qc_prep - Prepare taskfile for submission - * @qc: Metadata associated with taskfile to be prepared - * - * Prepare ATA taskfile for submission. - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ -void ata_sff_dumb_qc_prep(struct ata_queued_cmd *qc) -{ - if (!(qc->flags & ATA_QCFLAG_DMAMAP)) - return; - - ata_fill_sg_dumb(qc); -} -EXPORT_SYMBOL_GPL(ata_sff_dumb_qc_prep); - /** * ata_sff_check_status - Read device status reg & clear interrupt * @ap: port where the device is @@ -446,6 +281,27 @@ int ata_sff_wait_ready(struct ata_link *link, unsigned long deadline) EXPORT_SYMBOL_GPL(ata_sff_wait_ready); /** + * ata_sff_set_devctl - Write device control reg + * @ap: port where the device is + * @ctl: value to write + * + * Writes ATA taskfile device control register. + * + * Note: may NOT be used as the sff_set_devctl() entry in + * ata_port_operations. + * + * LOCKING: + * Inherited from caller. + */ +static void ata_sff_set_devctl(struct ata_port *ap, u8 ctl) +{ + if (ap->ops->sff_set_devctl) + ap->ops->sff_set_devctl(ap, ctl); + else + iowrite8(ctl, ap->ioaddr.ctl_addr); +} + +/** * ata_sff_dev_select - Select device 0/1 on ATA bus * @ap: ATA channel to manipulate * @device: ATA device (numbered from zero) to select @@ -491,7 +347,7 @@ EXPORT_SYMBOL_GPL(ata_sff_dev_select); * LOCKING: * caller. */ -void ata_dev_select(struct ata_port *ap, unsigned int device, +static void ata_dev_select(struct ata_port *ap, unsigned int device, unsigned int wait, unsigned int can_sleep) { if (ata_msg_probe(ap)) @@ -517,24 +373,29 @@ void ata_dev_select(struct ata_port *ap, unsigned int device, * Enable interrupts on a legacy IDE device using MMIO or PIO, * wait for idle, clear any pending interrupts. * + * Note: may NOT be used as the sff_irq_on() entry in + * ata_port_operations. + * * LOCKING: * Inherited from caller. */ -u8 ata_sff_irq_on(struct ata_port *ap) +void ata_sff_irq_on(struct ata_port *ap) { struct ata_ioports *ioaddr = &ap->ioaddr; - u8 tmp; + + if (ap->ops->sff_irq_on) { + ap->ops->sff_irq_on(ap); + return; + } ap->ctl &= ~ATA_NIEN; ap->last_ctl = ap->ctl; - if (ioaddr->ctl_addr) - iowrite8(ap->ctl, ioaddr->ctl_addr); - tmp = ata_wait_idle(ap); + if (ap->ops->sff_set_devctl || ioaddr->ctl_addr) + ata_sff_set_devctl(ap, ap->ctl); + ata_wait_idle(ap); ap->ops->sff_irq_clear(ap); - - return tmp; } EXPORT_SYMBOL_GPL(ata_sff_irq_on); @@ -579,7 +440,6 @@ void ata_sff_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) if (ioaddr->ctl_addr) iowrite8(tf->ctl, ioaddr->ctl_addr); ap->last_ctl = tf->ctl; - ata_wait_idle(ap); } if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { @@ -615,8 +475,6 @@ void ata_sff_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) iowrite8(tf->device, ioaddr->device_addr); VPRINTK("device 0x%X\n", tf->device); } - - ata_wait_idle(ap); } EXPORT_SYMBOL_GPL(ata_sff_tf_load); @@ -894,7 +752,7 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) do_write); } - if (!do_write) + if (!do_write && !PageSlab(page)) flush_dcache_page(page); qc->curbytes += qc->sect_size; @@ -1165,7 +1023,7 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) qc = ata_qc_from_tag(ap, qc->tag); if (qc) { if (likely(!(qc->err_mask & AC_ERR_HSM))) { - ap->ops->sff_irq_on(ap); + ata_sff_irq_on(ap); ata_qc_complete(qc); } else ata_port_freeze(ap); @@ -1181,7 +1039,7 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) } else { if (in_wq) { spin_lock_irqsave(ap->lock, flags); - ap->ops->sff_irq_on(ap); + ata_sff_irq_on(ap); ata_qc_complete(qc); spin_unlock_irqrestore(ap->lock, flags); } else @@ -1293,7 +1151,7 @@ fsm_start: if (in_wq) spin_unlock_irqrestore(ap->lock, flags); - /* if polling, ata_pio_task() handles the rest. + /* if polling, ata_sff_pio_task() handles the rest. * otherwise, interrupt handler takes over from here. */ break; @@ -1458,14 +1316,38 @@ fsm_start: } EXPORT_SYMBOL_GPL(ata_sff_hsm_move); -void ata_pio_task(struct work_struct *work) +void ata_sff_queue_pio_task(struct ata_port *ap, unsigned long delay) +{ + /* may fail if ata_sff_flush_pio_task() in progress */ + queue_delayed_work(ata_sff_wq, &ap->sff_pio_task, + msecs_to_jiffies(delay)); +} +EXPORT_SYMBOL_GPL(ata_sff_queue_pio_task); + +void ata_sff_flush_pio_task(struct ata_port *ap) +{ + DPRINTK("ENTER\n"); + + cancel_rearming_delayed_work(&ap->sff_pio_task); + ap->hsm_task_state = HSM_ST_IDLE; + + if (ata_msg_ctl(ap)) + ata_port_printk(ap, KERN_DEBUG, "%s: EXIT\n", __func__); +} + +static void ata_sff_pio_task(struct work_struct *work) { struct ata_port *ap = - container_of(work, struct ata_port, port_task.work); - struct ata_queued_cmd *qc = ap->port_task_data; + container_of(work, struct ata_port, sff_pio_task.work); + struct ata_queued_cmd *qc; u8 status; int poll_next; + /* qc can be NULL if timeout occurred */ + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (!qc) + return; + fsm_start: WARN_ON_ONCE(ap->hsm_task_state == HSM_ST_IDLE); @@ -1481,7 +1363,7 @@ fsm_start: msleep(2); status = ata_sff_busy_wait(ap, ATA_BUSY, 10); if (status & ATA_BUSY) { - ata_pio_queue_task(ap, qc, ATA_SHORT_PAUSE); + ata_sff_queue_pio_task(ap, ATA_SHORT_PAUSE); return; } } @@ -1497,15 +1379,11 @@ fsm_start: } /** - * ata_sff_qc_issue - issue taskfile to device in proto-dependent manner + * ata_sff_qc_issue - issue taskfile to a SFF controller * @qc: command to issue to device * - * Using various libata functions and hooks, this function - * starts an ATA command. ATA commands are grouped into - * classes called "protocols", and issuing each type of protocol - * is slightly different. - * - * May be used as the qc_issue() entry in ata_port_operations. + * This function issues a PIO or NODATA command to a SFF + * controller. * * LOCKING: * spin_lock_irqsave(host lock) @@ -1520,23 +1398,8 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) /* Use polling pio if the LLD doesn't handle * interrupt driven pio and atapi CDB interrupt. */ - if (ap->flags & ATA_FLAG_PIO_POLLING) { - switch (qc->tf.protocol) { - case ATA_PROT_PIO: - case ATA_PROT_NODATA: - case ATAPI_PROT_PIO: - case ATAPI_PROT_NODATA: - qc->tf.flags |= ATA_TFLAG_POLLING; - break; - case ATAPI_PROT_DMA: - if (qc->dev->flags & ATA_DFLAG_CDB_INTR) - /* see ata_dma_blacklisted() */ - BUG(); - break; - default: - break; - } - } + if (ap->flags & ATA_FLAG_PIO_POLLING) + qc->tf.flags |= ATA_TFLAG_POLLING; /* select the device */ ata_dev_select(ap, qc->dev->devno, 1, 0); @@ -1551,17 +1414,8 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) ap->hsm_task_state = HSM_ST_LAST; if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_pio_queue_task(ap, qc, 0); - - break; - - case ATA_PROT_DMA: - WARN_ON_ONCE(qc->tf.flags & ATA_TFLAG_POLLING); + ata_sff_queue_pio_task(ap, 0); - ap->ops->sff_tf_load(ap, &qc->tf); /* load tf registers */ - ap->ops->bmdma_setup(qc); /* set up bmdma */ - ap->ops->bmdma_start(qc); /* initiate bmdma */ - ap->hsm_task_state = HSM_ST_LAST; break; case ATA_PROT_PIO: @@ -1573,20 +1427,21 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) if (qc->tf.flags & ATA_TFLAG_WRITE) { /* PIO data out protocol */ ap->hsm_task_state = HSM_ST_FIRST; - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); - /* always send first data block using - * the ata_pio_task() codepath. + /* always send first data block using the + * ata_sff_pio_task() codepath. */ } else { /* PIO data in protocol */ ap->hsm_task_state = HSM_ST; if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); - /* if polling, ata_pio_task() handles the rest. - * otherwise, interrupt handler takes over from here. + /* if polling, ata_sff_pio_task() handles the + * rest. otherwise, interrupt handler takes + * over from here. */ } @@ -1604,19 +1459,7 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) /* send cdb by polling if no cdb interrupt */ if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) || (qc->tf.flags & ATA_TFLAG_POLLING)) - ata_pio_queue_task(ap, qc, 0); - break; - - case ATAPI_PROT_DMA: - WARN_ON_ONCE(qc->tf.flags & ATA_TFLAG_POLLING); - - ap->ops->sff_tf_load(ap, &qc->tf); /* load tf registers */ - ap->ops->bmdma_setup(qc); /* set up bmdma */ - ap->hsm_task_state = HSM_ST_FIRST; - - /* send cdb by polling if no cdb interrupt */ - if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); break; default: @@ -1728,7 +1571,7 @@ unsigned int ata_sff_host_intr(struct ata_port *ap, goto idle_irq; } - /* ack bmdma irq events */ + /* clear irq events */ ap->ops->sff_irq_clear(ap); ata_sff_hsm_move(ap, qc, status, 0); @@ -1785,9 +1628,6 @@ retry: struct ata_port *ap = host->ports[i]; struct ata_queued_cmd *qc; - if (unlikely(ap->flags & ATA_FLAG_DISABLED)) - continue; - qc = ata_qc_from_tag(ap, ap->link.active_tag); if (qc) { if (!(qc->tf.flags & ATA_TFLAG_POLLING)) @@ -1862,11 +1702,8 @@ void ata_sff_lost_interrupt(struct ata_port *ap) /* Only one outstanding command per SFF channel */ qc = ata_qc_from_tag(ap, ap->link.active_tag); - /* Check we have a live one.. */ - if (qc == NULL || !(qc->flags & ATA_QCFLAG_ACTIVE)) - return; - /* We cannot lose an interrupt on a polled command */ - if (qc->tf.flags & ATA_TFLAG_POLLING) + /* We cannot lose an interrupt on a non-existent or polled command */ + if (!qc || qc->tf.flags & ATA_TFLAG_POLLING) return; /* See if the controller thinks it is still busy - if so the command isn't a lost IRQ but is still in progress */ @@ -1888,20 +1725,18 @@ EXPORT_SYMBOL_GPL(ata_sff_lost_interrupt); * ata_sff_freeze - Freeze SFF controller port * @ap: port to freeze * - * Freeze BMDMA controller port. + * Freeze SFF controller port. * * LOCKING: * Inherited from caller. */ void ata_sff_freeze(struct ata_port *ap) { - struct ata_ioports *ioaddr = &ap->ioaddr; - ap->ctl |= ATA_NIEN; ap->last_ctl = ap->ctl; - if (ioaddr->ctl_addr) - iowrite8(ap->ctl, ioaddr->ctl_addr); + if (ap->ops->sff_set_devctl || ap->ioaddr.ctl_addr) + ata_sff_set_devctl(ap, ap->ctl); /* Under certain circumstances, some controllers raise IRQ on * ATA_NIEN manipulation. Also, many controllers fail to mask @@ -1927,7 +1762,7 @@ void ata_sff_thaw(struct ata_port *ap) /* clear & re-enable interrupts */ ap->ops->sff_check_status(ap); ap->ops->sff_irq_clear(ap); - ap->ops->sff_irq_on(ap); + ata_sff_irq_on(ap); } EXPORT_SYMBOL_GPL(ata_sff_thaw); @@ -2301,8 +2136,8 @@ void ata_sff_postreset(struct ata_link *link, unsigned int *classes) } /* set up device control */ - if (ap->ioaddr.ctl_addr) { - iowrite8(ap->ctl, ap->ioaddr.ctl_addr); + if (ap->ops->sff_set_devctl || ap->ioaddr.ctl_addr) { + ata_sff_set_devctl(ap, ap->ctl); ap->last_ctl = ap->ctl; } } @@ -2342,7 +2177,7 @@ void ata_sff_drain_fifo(struct ata_queued_cmd *qc) EXPORT_SYMBOL_GPL(ata_sff_drain_fifo); /** - * ata_sff_error_handler - Stock error handler for BMDMA controller + * ata_sff_error_handler - Stock error handler for SFF controller * @ap: port to handle error for * * Stock error handler for SFF controller. It can handle both @@ -2359,62 +2194,32 @@ void ata_sff_error_handler(struct ata_port *ap) ata_reset_fn_t hardreset = ap->ops->hardreset; struct ata_queued_cmd *qc; unsigned long flags; - int thaw = 0; qc = __ata_qc_from_tag(ap, ap->link.active_tag); if (qc && !(qc->flags & ATA_QCFLAG_FAILED)) qc = NULL; - /* reset PIO HSM and stop DMA engine */ spin_lock_irqsave(ap->lock, flags); - ap->hsm_task_state = HSM_ST_IDLE; - - if (ap->ioaddr.bmdma_addr && - qc && (qc->tf.protocol == ATA_PROT_DMA || - qc->tf.protocol == ATAPI_PROT_DMA)) { - u8 host_stat; - - host_stat = ap->ops->bmdma_status(ap); - - /* BMDMA controllers indicate host bus error by - * setting DMA_ERR bit and timing out. As it wasn't - * really a timeout event, adjust error mask and - * cancel frozen state. - */ - if (qc->err_mask == AC_ERR_TIMEOUT - && (host_stat & ATA_DMA_ERR)) { - qc->err_mask = AC_ERR_HOST_BUS; - thaw = 1; - } - - ap->ops->bmdma_stop(qc); - } - - ata_sff_sync(ap); /* FIXME: We don't need this */ - ap->ops->sff_check_status(ap); - ap->ops->sff_irq_clear(ap); - /* We *MUST* do FIFO draining before we issue a reset as several - * devices helpfully clear their internal state and will lock solid - * if we touch the data port post reset. Pass qc in case anyone wants - * to do different PIO/DMA recovery or has per command fixups + /* + * We *MUST* do FIFO draining before we issue a reset as + * several devices helpfully clear their internal state and + * will lock solid if we touch the data port post reset. Pass + * qc in case anyone wants to do different PIO/DMA recovery or + * has per command fixups */ - if (ap->ops->drain_fifo) - ap->ops->drain_fifo(qc); + if (ap->ops->sff_drain_fifo) + ap->ops->sff_drain_fifo(qc); spin_unlock_irqrestore(ap->lock, flags); - if (thaw) - ata_eh_thaw_port(ap); - - /* PIO and DMA engines have been stopped, perform recovery */ - - /* Ignore ata_sff_softreset if ctl isn't accessible and - * built-in hardresets if SCR access isn't available. - */ + /* ignore ata_sff_softreset if ctl isn't accessible */ if (softreset == ata_sff_softreset && !ap->ioaddr.ctl_addr) softreset = NULL; - if (ata_is_builtin_hardreset(hardreset) && !sata_scr_valid(&ap->link)) + + /* ignore built-in hardresets if SCR access is not available */ + if ((hardreset == sata_std_hardreset || + hardreset == sata_sff_hardreset) && !sata_scr_valid(&ap->link)) hardreset = NULL; ata_do_eh(ap, ap->ops->prereset, softreset, hardreset, @@ -2423,73 +2228,6 @@ void ata_sff_error_handler(struct ata_port *ap) EXPORT_SYMBOL_GPL(ata_sff_error_handler); /** - * ata_sff_post_internal_cmd - Stock post_internal_cmd for SFF controller - * @qc: internal command to clean up - * - * LOCKING: - * Kernel thread context (may sleep) - */ -void ata_sff_post_internal_cmd(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - unsigned long flags; - - spin_lock_irqsave(ap->lock, flags); - - ap->hsm_task_state = HSM_ST_IDLE; - - if (ap->ioaddr.bmdma_addr) - ap->ops->bmdma_stop(qc); - - spin_unlock_irqrestore(ap->lock, flags); -} -EXPORT_SYMBOL_GPL(ata_sff_post_internal_cmd); - -/** - * ata_sff_port_start - Set port up for dma. - * @ap: Port to initialize - * - * Called just after data structures for each port are - * initialized. Allocates space for PRD table if the device - * is DMA capable SFF. - * - * May be used as the port_start() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -int ata_sff_port_start(struct ata_port *ap) -{ - if (ap->ioaddr.bmdma_addr) - return ata_port_start(ap); - return 0; -} -EXPORT_SYMBOL_GPL(ata_sff_port_start); - -/** - * ata_sff_port_start32 - Set port up for dma. - * @ap: Port to initialize - * - * Called just after data structures for each port are - * initialized. Allocates space for PRD table if the device - * is DMA capable SFF. - * - * May be used as the port_start() entry in ata_port_operations for - * devices that are capable of 32bit PIO. - * - * LOCKING: - * Inherited from caller. - */ -int ata_sff_port_start32(struct ata_port *ap) -{ - ap->pflags |= ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE; - if (ap->ioaddr.bmdma_addr) - return ata_port_start(ap); - return 0; -} -EXPORT_SYMBOL_GPL(ata_sff_port_start32); - -/** * ata_sff_std_ports - initialize ioaddr with standard port offsets. * @ioaddr: IO address structure to be initialized * @@ -2515,302 +2253,8 @@ void ata_sff_std_ports(struct ata_ioports *ioaddr) } EXPORT_SYMBOL_GPL(ata_sff_std_ports); -unsigned long ata_bmdma_mode_filter(struct ata_device *adev, - unsigned long xfer_mask) -{ - /* Filter out DMA modes if the device has been configured by - the BIOS as PIO only */ - - if (adev->link->ap->ioaddr.bmdma_addr == NULL) - xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); - return xfer_mask; -} -EXPORT_SYMBOL_GPL(ata_bmdma_mode_filter); - -/** - * ata_bmdma_setup - Set up PCI IDE BMDMA transaction - * @qc: Info associated with this ATA transaction. - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ -void ata_bmdma_setup(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); - u8 dmactl; - - /* load PRD table addr. */ - mb(); /* make sure PRD table writes are visible to controller */ - iowrite32(ap->prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS); - - /* specify data direction, triple-check start bit is clear */ - dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); - if (!rw) - dmactl |= ATA_DMA_WR; - iowrite8(dmactl, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - - /* issue r/w command */ - ap->ops->sff_exec_command(ap, &qc->tf); -} -EXPORT_SYMBOL_GPL(ata_bmdma_setup); - -/** - * ata_bmdma_start - Start a PCI IDE BMDMA transaction - * @qc: Info associated with this ATA transaction. - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ -void ata_bmdma_start(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - u8 dmactl; - - /* start host DMA transaction */ - dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - iowrite8(dmactl | ATA_DMA_START, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - - /* Strictly, one may wish to issue an ioread8() here, to - * flush the mmio write. However, control also passes - * to the hardware at this point, and it will interrupt - * us when we are to resume control. So, in effect, - * we don't care when the mmio write flushes. - * Further, a read of the DMA status register _immediately_ - * following the write may not be what certain flaky hardware - * is expected, so I think it is best to not add a readb() - * without first all the MMIO ATA cards/mobos. - * Or maybe I'm just being paranoid. - * - * FIXME: The posting of this write means I/O starts are - * unneccessarily delayed for MMIO - */ -} -EXPORT_SYMBOL_GPL(ata_bmdma_start); - -/** - * ata_bmdma_stop - Stop PCI IDE BMDMA transfer - * @qc: Command we are ending DMA for - * - * Clears the ATA_DMA_START flag in the dma control register - * - * May be used as the bmdma_stop() entry in ata_port_operations. - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ -void ata_bmdma_stop(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void __iomem *mmio = ap->ioaddr.bmdma_addr; - - /* clear start/stop bit */ - iowrite8(ioread8(mmio + ATA_DMA_CMD) & ~ATA_DMA_START, - mmio + ATA_DMA_CMD); - - /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ - ata_sff_dma_pause(ap); -} -EXPORT_SYMBOL_GPL(ata_bmdma_stop); - -/** - * ata_bmdma_status - Read PCI IDE BMDMA status - * @ap: Port associated with this ATA transaction. - * - * Read and return BMDMA status register. - * - * May be used as the bmdma_status() entry in ata_port_operations. - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ -u8 ata_bmdma_status(struct ata_port *ap) -{ - return ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); -} -EXPORT_SYMBOL_GPL(ata_bmdma_status); - -/** - * ata_bus_reset - reset host port and associated ATA channel - * @ap: port to reset - * - * This is typically the first time we actually start issuing - * commands to the ATA channel. We wait for BSY to clear, then - * issue EXECUTE DEVICE DIAGNOSTIC command, polling for its - * result. Determine what devices, if any, are on the channel - * by looking at the device 0/1 error register. Look at the signature - * stored in each device's taskfile registers, to determine if - * the device is ATA or ATAPI. - * - * LOCKING: - * PCI/etc. bus probe sem. - * Obtains host lock. - * - * SIDE EFFECTS: - * Sets ATA_FLAG_DISABLED if bus reset fails. - * - * DEPRECATED: - * This function is only for drivers which still use old EH and - * will be removed soon. - */ -void ata_bus_reset(struct ata_port *ap) -{ - struct ata_device *device = ap->link.device; - struct ata_ioports *ioaddr = &ap->ioaddr; - unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; - u8 err; - unsigned int dev0, dev1 = 0, devmask = 0; - int rc; - - DPRINTK("ENTER, host %u, port %u\n", ap->print_id, ap->port_no); - - /* determine if device 0/1 are present */ - if (ap->flags & ATA_FLAG_SATA_RESET) - dev0 = 1; - else { - dev0 = ata_devchk(ap, 0); - if (slave_possible) - dev1 = ata_devchk(ap, 1); - } - - if (dev0) - devmask |= (1 << 0); - if (dev1) - devmask |= (1 << 1); - - /* select device 0 again */ - ap->ops->sff_dev_select(ap, 0); - - /* issue bus reset */ - if (ap->flags & ATA_FLAG_SRST) { - rc = ata_bus_softreset(ap, devmask, - ata_deadline(jiffies, 40000)); - if (rc && rc != -ENODEV) - goto err_out; - } - - /* - * determine by signature whether we have ATA or ATAPI devices - */ - device[0].class = ata_sff_dev_classify(&device[0], dev0, &err); - if ((slave_possible) && (err != 0x81)) - device[1].class = ata_sff_dev_classify(&device[1], dev1, &err); - - /* is double-select really necessary? */ - if (device[1].class != ATA_DEV_NONE) - ap->ops->sff_dev_select(ap, 1); - if (device[0].class != ATA_DEV_NONE) - ap->ops->sff_dev_select(ap, 0); - - /* if no devices were detected, disable this port */ - if ((device[0].class == ATA_DEV_NONE) && - (device[1].class == ATA_DEV_NONE)) - goto err_out; - - if (ap->flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST)) { - /* set up device control for ATA_FLAG_SATA_RESET */ - iowrite8(ap->ctl, ioaddr->ctl_addr); - ap->last_ctl = ap->ctl; - } - - DPRINTK("EXIT\n"); - return; - -err_out: - ata_port_printk(ap, KERN_ERR, "disabling port\n"); - ata_port_disable(ap); - - DPRINTK("EXIT\n"); -} -EXPORT_SYMBOL_GPL(ata_bus_reset); - #ifdef CONFIG_PCI -/** - * ata_pci_bmdma_clear_simplex - attempt to kick device out of simplex - * @pdev: PCI device - * - * Some PCI ATA devices report simplex mode but in fact can be told to - * enter non simplex mode. This implements the necessary logic to - * perform the task on such devices. Calling it on other devices will - * have -undefined- behaviour. - */ -int ata_pci_bmdma_clear_simplex(struct pci_dev *pdev) -{ - unsigned long bmdma = pci_resource_start(pdev, 4); - u8 simplex; - - if (bmdma == 0) - return -ENOENT; - - simplex = inb(bmdma + 0x02); - outb(simplex & 0x60, bmdma + 0x02); - simplex = inb(bmdma + 0x02); - if (simplex & 0x80) - return -EOPNOTSUPP; - return 0; -} -EXPORT_SYMBOL_GPL(ata_pci_bmdma_clear_simplex); - -/** - * ata_pci_bmdma_init - acquire PCI BMDMA resources and init ATA host - * @host: target ATA host - * - * Acquire PCI BMDMA resources and initialize @host accordingly. - * - * LOCKING: - * Inherited from calling layer (may sleep). - * - * RETURNS: - * 0 on success, -errno otherwise. - */ -int ata_pci_bmdma_init(struct ata_host *host) -{ - struct device *gdev = host->dev; - struct pci_dev *pdev = to_pci_dev(gdev); - int i, rc; - - /* No BAR4 allocation: No DMA */ - if (pci_resource_start(pdev, 4) == 0) - return 0; - - /* TODO: If we get no DMA mask we should fall back to PIO */ - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - - /* request and iomap DMA region */ - rc = pcim_iomap_regions(pdev, 1 << 4, dev_driver_string(gdev)); - if (rc) { - dev_printk(KERN_ERR, gdev, "failed to request/iomap BAR4\n"); - return -ENOMEM; - } - host->iomap = pcim_iomap_table(pdev); - - for (i = 0; i < 2; i++) { - struct ata_port *ap = host->ports[i]; - void __iomem *bmdma = host->iomap[4] + 8 * i; - - if (ata_port_is_dummy(ap)) - continue; - - ap->ioaddr.bmdma_addr = bmdma; - if ((!(ap->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(bmdma + 2) & 0x80)) - host->flags |= ATA_HOST_SIMPLEX; - - ata_port_desc(ap, "bmdma 0x%llx", - (unsigned long long)pci_resource_start(pdev, 4) + 8 * i); - } - - return 0; -} -EXPORT_SYMBOL_GPL(ata_pci_bmdma_init); - static int ata_resources_present(struct pci_dev *pdev, int port) { int i; @@ -2942,21 +2386,12 @@ int ata_pci_sff_prepare_host(struct pci_dev *pdev, goto err_out; /* init DMA related stuff */ - rc = ata_pci_bmdma_init(host); - if (rc) - goto err_bmdma; + ata_pci_bmdma_init(host); devres_remove_group(&pdev->dev, NULL); *r_host = host; return 0; -err_bmdma: - /* This is necessary because PCI and iomap resources are - * merged and releasing the top group won't release the - * acquired resources if some of those have been acquired - * before entering this function. - */ - pcim_iounmap_regions(pdev, 0xf); err_out: devres_release_group(&pdev->dev, NULL); return rc; @@ -3135,3 +2570,609 @@ out: EXPORT_SYMBOL_GPL(ata_pci_sff_init_one); #endif /* CONFIG_PCI */ + +const struct ata_port_operations ata_bmdma_port_ops = { + .inherits = &ata_sff_port_ops, + + .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + + .qc_prep = ata_bmdma_qc_prep, + .qc_issue = ata_bmdma_qc_issue, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .port_start = ata_bmdma_port_start, +}; +EXPORT_SYMBOL_GPL(ata_bmdma_port_ops); + +const struct ata_port_operations ata_bmdma32_port_ops = { + .inherits = &ata_bmdma_port_ops, + + .sff_data_xfer = ata_sff_data_xfer32, + .port_start = ata_bmdma_port_start32, +}; +EXPORT_SYMBOL_GPL(ata_bmdma32_port_ops); + +/** + * ata_bmdma_fill_sg - Fill PCI IDE PRD table + * @qc: Metadata associated with taskfile to be transferred + * + * Fill PCI IDE PRD (scatter-gather) table with segments + * associated with the current disk command. + * + * LOCKING: + * spin_lock_irqsave(host lock) + * + */ +static void ata_bmdma_fill_sg(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_bmdma_prd *prd = ap->bmdma_prd; + struct scatterlist *sg; + unsigned int si, pi; + + pi = 0; + for_each_sg(qc->sg, sg, qc->n_elem, si) { + u32 addr, offset; + u32 sg_len, len; + + /* determine if physical DMA addr spans 64K boundary. + * Note h/w doesn't support 64-bit, so we unconditionally + * truncate dma_addr_t to u32. + */ + addr = (u32) sg_dma_address(sg); + sg_len = sg_dma_len(sg); + + while (sg_len) { + offset = addr & 0xffff; + len = sg_len; + if ((offset + sg_len) > 0x10000) + len = 0x10000 - offset; + + prd[pi].addr = cpu_to_le32(addr); + prd[pi].flags_len = cpu_to_le32(len & 0xffff); + VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, len); + + pi++; + sg_len -= len; + addr += len; + } + } + + prd[pi - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); +} + +/** + * ata_bmdma_fill_sg_dumb - Fill PCI IDE PRD table + * @qc: Metadata associated with taskfile to be transferred + * + * Fill PCI IDE PRD (scatter-gather) table with segments + * associated with the current disk command. Perform the fill + * so that we avoid writing any length 64K records for + * controllers that don't follow the spec. + * + * LOCKING: + * spin_lock_irqsave(host lock) + * + */ +static void ata_bmdma_fill_sg_dumb(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_bmdma_prd *prd = ap->bmdma_prd; + struct scatterlist *sg; + unsigned int si, pi; + + pi = 0; + for_each_sg(qc->sg, sg, qc->n_elem, si) { + u32 addr, offset; + u32 sg_len, len, blen; + + /* determine if physical DMA addr spans 64K boundary. + * Note h/w doesn't support 64-bit, so we unconditionally + * truncate dma_addr_t to u32. + */ + addr = (u32) sg_dma_address(sg); + sg_len = sg_dma_len(sg); + + while (sg_len) { + offset = addr & 0xffff; + len = sg_len; + if ((offset + sg_len) > 0x10000) + len = 0x10000 - offset; + + blen = len & 0xffff; + prd[pi].addr = cpu_to_le32(addr); + if (blen == 0) { + /* Some PATA chipsets like the CS5530 can't + cope with 0x0000 meaning 64K as the spec + says */ + prd[pi].flags_len = cpu_to_le32(0x8000); + blen = 0x8000; + prd[++pi].addr = cpu_to_le32(addr + 0x8000); + } + prd[pi].flags_len = cpu_to_le32(blen); + VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, len); + + pi++; + sg_len -= len; + addr += len; + } + } + + prd[pi - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); +} + +/** + * ata_bmdma_qc_prep - Prepare taskfile for submission + * @qc: Metadata associated with taskfile to be prepared + * + * Prepare ATA taskfile for submission. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_bmdma_qc_prep(struct ata_queued_cmd *qc) +{ + if (!(qc->flags & ATA_QCFLAG_DMAMAP)) + return; + + ata_bmdma_fill_sg(qc); +} +EXPORT_SYMBOL_GPL(ata_bmdma_qc_prep); + +/** + * ata_bmdma_dumb_qc_prep - Prepare taskfile for submission + * @qc: Metadata associated with taskfile to be prepared + * + * Prepare ATA taskfile for submission. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_bmdma_dumb_qc_prep(struct ata_queued_cmd *qc) +{ + if (!(qc->flags & ATA_QCFLAG_DMAMAP)) + return; + + ata_bmdma_fill_sg_dumb(qc); +} +EXPORT_SYMBOL_GPL(ata_bmdma_dumb_qc_prep); + +/** + * ata_bmdma_qc_issue - issue taskfile to a BMDMA controller + * @qc: command to issue to device + * + * This function issues a PIO, NODATA or DMA command to a + * SFF/BMDMA controller. PIO and NODATA are handled by + * ata_sff_qc_issue(). + * + * LOCKING: + * spin_lock_irqsave(host lock) + * + * RETURNS: + * Zero on success, AC_ERR_* mask on failure + */ +unsigned int ata_bmdma_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + + /* see ata_dma_blacklisted() */ + BUG_ON((ap->flags & ATA_FLAG_PIO_POLLING) && + qc->tf.protocol == ATAPI_PROT_DMA); + + /* defer PIO handling to sff_qc_issue */ + if (!ata_is_dma(qc->tf.protocol)) + return ata_sff_qc_issue(qc); + + /* select the device */ + ata_dev_select(ap, qc->dev->devno, 1, 0); + + /* start the command */ + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + WARN_ON_ONCE(qc->tf.flags & ATA_TFLAG_POLLING); + + ap->ops->sff_tf_load(ap, &qc->tf); /* load tf registers */ + ap->ops->bmdma_setup(qc); /* set up bmdma */ + ap->ops->bmdma_start(qc); /* initiate bmdma */ + ap->hsm_task_state = HSM_ST_LAST; + break; + + case ATAPI_PROT_DMA: + WARN_ON_ONCE(qc->tf.flags & ATA_TFLAG_POLLING); + + ap->ops->sff_tf_load(ap, &qc->tf); /* load tf registers */ + ap->ops->bmdma_setup(qc); /* set up bmdma */ + ap->hsm_task_state = HSM_ST_FIRST; + + /* send cdb by polling if no cdb interrupt */ + if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) + ata_sff_queue_pio_task(ap, 0); + break; + + default: + WARN_ON(1); + return AC_ERR_SYSTEM; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ata_bmdma_qc_issue); + +/** + * ata_bmdma_error_handler - Stock error handler for BMDMA controller + * @ap: port to handle error for + * + * Stock error handler for BMDMA controller. It can handle both + * PATA and SATA controllers. Most BMDMA controllers should be + * able to use this EH as-is or with some added handling before + * and after. + * + * LOCKING: + * Kernel thread context (may sleep) + */ +void ata_bmdma_error_handler(struct ata_port *ap) +{ + struct ata_queued_cmd *qc; + unsigned long flags; + bool thaw = false; + + qc = __ata_qc_from_tag(ap, ap->link.active_tag); + if (qc && !(qc->flags & ATA_QCFLAG_FAILED)) + qc = NULL; + + /* reset PIO HSM and stop DMA engine */ + spin_lock_irqsave(ap->lock, flags); + + if (qc && ata_is_dma(qc->tf.protocol)) { + u8 host_stat; + + host_stat = ap->ops->bmdma_status(ap); + + /* BMDMA controllers indicate host bus error by + * setting DMA_ERR bit and timing out. As it wasn't + * really a timeout event, adjust error mask and + * cancel frozen state. + */ + if (qc->err_mask == AC_ERR_TIMEOUT && (host_stat & ATA_DMA_ERR)) { + qc->err_mask = AC_ERR_HOST_BUS; + thaw = true; + } + + ap->ops->bmdma_stop(qc); + + /* if we're gonna thaw, make sure IRQ is clear */ + if (thaw) { + ap->ops->sff_check_status(ap); + ap->ops->sff_irq_clear(ap); + } + } + + spin_unlock_irqrestore(ap->lock, flags); + + if (thaw) + ata_eh_thaw_port(ap); + + ata_sff_error_handler(ap); +} +EXPORT_SYMBOL_GPL(ata_bmdma_error_handler); + +/** + * ata_bmdma_post_internal_cmd - Stock post_internal_cmd for BMDMA + * @qc: internal command to clean up + * + * LOCKING: + * Kernel thread context (may sleep) + */ +void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned long flags; + + if (ata_is_dma(qc->tf.protocol)) { + spin_lock_irqsave(ap->lock, flags); + ap->ops->bmdma_stop(qc); + spin_unlock_irqrestore(ap->lock, flags); + } +} +EXPORT_SYMBOL_GPL(ata_bmdma_post_internal_cmd); + +/** + * ata_bmdma_setup - Set up PCI IDE BMDMA transaction + * @qc: Info associated with this ATA transaction. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_bmdma_setup(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); + u8 dmactl; + + /* load PRD table addr. */ + mb(); /* make sure PRD table writes are visible to controller */ + iowrite32(ap->bmdma_prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS); + + /* specify data direction, triple-check start bit is clear */ + dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); + if (!rw) + dmactl |= ATA_DMA_WR; + iowrite8(dmactl, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + + /* issue r/w command */ + ap->ops->sff_exec_command(ap, &qc->tf); +} +EXPORT_SYMBOL_GPL(ata_bmdma_setup); + +/** + * ata_bmdma_start - Start a PCI IDE BMDMA transaction + * @qc: Info associated with this ATA transaction. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_bmdma_start(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + u8 dmactl; + + /* start host DMA transaction */ + dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + iowrite8(dmactl | ATA_DMA_START, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + + /* Strictly, one may wish to issue an ioread8() here, to + * flush the mmio write. However, control also passes + * to the hardware at this point, and it will interrupt + * us when we are to resume control. So, in effect, + * we don't care when the mmio write flushes. + * Further, a read of the DMA status register _immediately_ + * following the write may not be what certain flaky hardware + * is expected, so I think it is best to not add a readb() + * without first all the MMIO ATA cards/mobos. + * Or maybe I'm just being paranoid. + * + * FIXME: The posting of this write means I/O starts are + * unneccessarily delayed for MMIO + */ +} +EXPORT_SYMBOL_GPL(ata_bmdma_start); + +/** + * ata_bmdma_stop - Stop PCI IDE BMDMA transfer + * @qc: Command we are ending DMA for + * + * Clears the ATA_DMA_START flag in the dma control register + * + * May be used as the bmdma_stop() entry in ata_port_operations. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void ata_bmdma_stop(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void __iomem *mmio = ap->ioaddr.bmdma_addr; + + /* clear start/stop bit */ + iowrite8(ioread8(mmio + ATA_DMA_CMD) & ~ATA_DMA_START, + mmio + ATA_DMA_CMD); + + /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ + ata_sff_dma_pause(ap); +} +EXPORT_SYMBOL_GPL(ata_bmdma_stop); + +/** + * ata_bmdma_status - Read PCI IDE BMDMA status + * @ap: Port associated with this ATA transaction. + * + * Read and return BMDMA status register. + * + * May be used as the bmdma_status() entry in ata_port_operations. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +u8 ata_bmdma_status(struct ata_port *ap) +{ + return ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); +} +EXPORT_SYMBOL_GPL(ata_bmdma_status); + + +/** + * ata_bmdma_port_start - Set port up for bmdma. + * @ap: Port to initialize + * + * Called just after data structures for each port are + * initialized. Allocates space for PRD table. + * + * May be used as the port_start() entry in ata_port_operations. + * + * LOCKING: + * Inherited from caller. + */ +int ata_bmdma_port_start(struct ata_port *ap) +{ + if (ap->mwdma_mask || ap->udma_mask) { + ap->bmdma_prd = + dmam_alloc_coherent(ap->host->dev, ATA_PRD_TBL_SZ, + &ap->bmdma_prd_dma, GFP_KERNEL); + if (!ap->bmdma_prd) + return -ENOMEM; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ata_bmdma_port_start); + +/** + * ata_bmdma_port_start32 - Set port up for dma. + * @ap: Port to initialize + * + * Called just after data structures for each port are + * initialized. Enables 32bit PIO and allocates space for PRD + * table. + * + * May be used as the port_start() entry in ata_port_operations for + * devices that are capable of 32bit PIO. + * + * LOCKING: + * Inherited from caller. + */ +int ata_bmdma_port_start32(struct ata_port *ap) +{ + ap->pflags |= ATA_PFLAG_PIO32 | ATA_PFLAG_PIO32CHANGE; + return ata_bmdma_port_start(ap); +} +EXPORT_SYMBOL_GPL(ata_bmdma_port_start32); + +#ifdef CONFIG_PCI + +/** + * ata_pci_bmdma_clear_simplex - attempt to kick device out of simplex + * @pdev: PCI device + * + * Some PCI ATA devices report simplex mode but in fact can be told to + * enter non simplex mode. This implements the necessary logic to + * perform the task on such devices. Calling it on other devices will + * have -undefined- behaviour. + */ +int ata_pci_bmdma_clear_simplex(struct pci_dev *pdev) +{ + unsigned long bmdma = pci_resource_start(pdev, 4); + u8 simplex; + + if (bmdma == 0) + return -ENOENT; + + simplex = inb(bmdma + 0x02); + outb(simplex & 0x60, bmdma + 0x02); + simplex = inb(bmdma + 0x02); + if (simplex & 0x80) + return -EOPNOTSUPP; + return 0; +} +EXPORT_SYMBOL_GPL(ata_pci_bmdma_clear_simplex); + +static void ata_bmdma_nodma(struct ata_host *host, const char *reason) +{ + int i; + + dev_printk(KERN_ERR, host->dev, "BMDMA: %s, falling back to PIO\n", + reason); + + for (i = 0; i < 2; i++) { + host->ports[i]->mwdma_mask = 0; + host->ports[i]->udma_mask = 0; + } +} + +/** + * ata_pci_bmdma_init - acquire PCI BMDMA resources and init ATA host + * @host: target ATA host + * + * Acquire PCI BMDMA resources and initialize @host accordingly. + * + * LOCKING: + * Inherited from calling layer (may sleep). + */ +void ata_pci_bmdma_init(struct ata_host *host) +{ + struct device *gdev = host->dev; + struct pci_dev *pdev = to_pci_dev(gdev); + int i, rc; + + /* No BAR4 allocation: No DMA */ + if (pci_resource_start(pdev, 4) == 0) { + ata_bmdma_nodma(host, "BAR4 is zero"); + return; + } + + /* + * Some controllers require BMDMA region to be initialized + * even if DMA is not in use to clear IRQ status via + * ->sff_irq_clear method. Try to initialize bmdma_addr + * regardless of dma masks. + */ + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + ata_bmdma_nodma(host, "failed to set dma mask"); + if (!rc) { + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + ata_bmdma_nodma(host, + "failed to set consistent dma mask"); + } + + /* request and iomap DMA region */ + rc = pcim_iomap_regions(pdev, 1 << 4, dev_driver_string(gdev)); + if (rc) { + ata_bmdma_nodma(host, "failed to request/iomap BAR4"); + return; + } + host->iomap = pcim_iomap_table(pdev); + + for (i = 0; i < 2; i++) { + struct ata_port *ap = host->ports[i]; + void __iomem *bmdma = host->iomap[4] + 8 * i; + + if (ata_port_is_dummy(ap)) + continue; + + ap->ioaddr.bmdma_addr = bmdma; + if ((!(ap->flags & ATA_FLAG_IGN_SIMPLEX)) && + (ioread8(bmdma + 2) & 0x80)) + host->flags |= ATA_HOST_SIMPLEX; + + ata_port_desc(ap, "bmdma 0x%llx", + (unsigned long long)pci_resource_start(pdev, 4) + 8 * i); + } +} +EXPORT_SYMBOL_GPL(ata_pci_bmdma_init); + +#endif /* CONFIG_PCI */ + +/** + * ata_sff_port_init - Initialize SFF/BMDMA ATA port + * @ap: Port to initialize + * + * Called on port allocation to initialize SFF/BMDMA specific + * fields. + * + * LOCKING: + * None. + */ +void ata_sff_port_init(struct ata_port *ap) +{ + INIT_DELAYED_WORK(&ap->sff_pio_task, ata_sff_pio_task); + ap->ctl = ATA_DEVCTL_OBS; + ap->last_ctl = 0xFF; +} + +int __init ata_sff_init(void) +{ + /* + * FIXME: In UP case, there is only one workqueue thread and if you + * have more than one PIO device, latency is bloody awful, with + * occasional multi-second "hiccups" as one PIO device waits for + * another. It's an ugly wart that users DO occasionally complain + * about; luckily most users have at most one PIO polled device. + */ + ata_sff_wq = create_workqueue("ata_sff"); + if (!ata_sff_wq) + return -ENOMEM; + + return 0; +} + +void __exit ata_sff_exit(void) +{ + destroy_workqueue(ata_sff_wq); +} diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 823e63096362..4b84ed60324a 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -38,17 +38,6 @@ struct ata_scsi_args { void (*done)(struct scsi_cmnd *); }; -static inline int ata_is_builtin_hardreset(ata_reset_fn_t reset) -{ - if (reset == sata_std_hardreset) - return 1; -#ifdef CONFIG_ATA_SFF - if (reset == sata_sff_hardreset) - return 1; -#endif - return 0; -} - /* libata-core.c */ enum { /* flags for ata_dev_read_id() */ @@ -79,7 +68,6 @@ extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, unsigned int tag); extern u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev); -extern void ata_port_flush_task(struct ata_port *ap); extern unsigned ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, const u8 *cdb, int dma_dir, void *buf, unsigned int buflen, @@ -202,10 +190,19 @@ static inline int sata_pmp_attach(struct ata_device *dev) /* libata-sff.c */ #ifdef CONFIG_ATA_SFF -extern void ata_dev_select(struct ata_port *ap, unsigned int device, - unsigned int wait, unsigned int can_sleep); -extern u8 ata_irq_on(struct ata_port *ap); -extern void ata_pio_task(struct work_struct *work); +extern void ata_sff_flush_pio_task(struct ata_port *ap); +extern void ata_sff_port_init(struct ata_port *ap); +extern int ata_sff_init(void); +extern void ata_sff_exit(void); +#else /* CONFIG_ATA_SFF */ +static inline void ata_sff_flush_pio_task(struct ata_port *ap) +{ } +static inline void ata_sff_port_init(struct ata_port *ap) +{ } +static inline int ata_sff_init(void) +{ return 0; } +static inline void ata_sff_exit(void) +{ } #endif /* CONFIG_ATA_SFF */ #endif /* __LIBATA_H__ */ diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index 1ea2be0f4b94..066b9f301ed5 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -101,7 +101,7 @@ static unsigned long pacpi_discover_modes(struct ata_port *ap, struct ata_device static unsigned long pacpi_mode_filter(struct ata_device *adev, unsigned long mask) { struct pata_acpi *acpi = adev->link->ap->private_data; - return ata_bmdma_mode_filter(adev, mask & acpi->mask[adev->devno]); + return mask & acpi->mask[adev->devno]; } /** @@ -172,7 +172,7 @@ static unsigned int pacpi_qc_issue(struct ata_queued_cmd *qc) struct pata_acpi *acpi = ap->private_data; if (acpi->gtm.flags & 0x10) - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); if (adev != acpi->last) { pacpi_set_piomode(ap, adev); @@ -180,7 +180,7 @@ static unsigned int pacpi_qc_issue(struct ata_queued_cmd *qc) pacpi_set_dmamode(ap, adev); acpi->last = adev; } - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } /** @@ -205,7 +205,7 @@ static int pacpi_port_start(struct ata_port *ap) return -ENOMEM; acpi->mask[0] = pacpi_discover_modes(ap, &ap->link.device[0]); acpi->mask[1] = pacpi_discover_modes(ap, &ap->link.device[1]); - ret = ata_sff_port_start(ap); + ret = ata_bmdma_port_start(ap); if (ret < 0) return ret; diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index dc61b72f751c..f306e10c748d 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -124,7 +124,7 @@ static unsigned long ali_20_filter(struct ata_device *adev, unsigned long mask) ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); if (strstr(model_num, "WDC")) return mask &= ~ATA_MASK_UDMA; - return ata_bmdma_mode_filter(adev, mask); + return mask; } /** diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index c6a946aa252c..0da0dcc7dd08 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -202,7 +202,6 @@ static struct ata_port_operations pata_at91_port_ops = { .sff_data_xfer = pata_at91_data_xfer_noirq, .set_piomode = pata_at91_set_piomode, .cable_detect = ata_cable_40wire, - .port_start = ATA_OP_NULL, }; static int __devinit pata_at91_probe(struct platform_device *pdev) diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index cbaf2eddac6b..44d88b380ddd 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -217,7 +217,7 @@ static struct scsi_host_template atiixp_sht = { static struct ata_port_operations atiixp_port_ops = { .inherits = &ata_bmdma_port_ops, - .qc_prep = ata_sff_dumb_qc_prep, + .qc_prep = ata_bmdma_dumb_qc_prep, .bmdma_start = atiixp_bmdma_start, .bmdma_stop = atiixp_bmdma_stop, diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index 02c81f12c702..6422cfd13d0d 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -821,6 +821,18 @@ static void bfin_dev_select(struct ata_port *ap, unsigned int device) } /** + * bfin_set_devctl - Write device control reg + * @ap: port where the device is + * @ctl: value to write + */ + +static u8 bfin_set_devctl(struct ata_port *ap, u8 ctl) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + write_atapi_register(base, ATA_REG_CTRL, ctl); +} + +/** * bfin_bmdma_setup - Set up IDE DMA transaction * @qc: Info associated with this ATA transaction. * @@ -1216,56 +1228,6 @@ static void bfin_irq_clear(struct ata_port *ap) } /** - * bfin_irq_on - Enable interrupts on a port. - * @ap: Port on which interrupts are enabled. - * - * Note: Original code is ata_sff_irq_on(). - */ - -static unsigned char bfin_irq_on(struct ata_port *ap) -{ - void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; - u8 tmp; - - dev_dbg(ap->dev, "in atapi irq on\n"); - ap->ctl &= ~ATA_NIEN; - ap->last_ctl = ap->ctl; - - write_atapi_register(base, ATA_REG_CTRL, ap->ctl); - tmp = ata_wait_idle(ap); - - bfin_irq_clear(ap); - - return tmp; -} - -/** - * bfin_freeze - Freeze DMA controller port - * @ap: port to freeze - * - * Note: Original code is ata_sff_freeze(). - */ - -static void bfin_freeze(struct ata_port *ap) -{ - void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; - - dev_dbg(ap->dev, "in atapi dma freeze\n"); - ap->ctl |= ATA_NIEN; - ap->last_ctl = ap->ctl; - - write_atapi_register(base, ATA_REG_CTRL, ap->ctl); - - /* Under certain circumstances, some controllers raise IRQ on - * ATA_NIEN manipulation. Also, many controllers fail to mask - * previously pending IRQ on ATA_NIEN assertion. Clear it. - */ - ap->ops->sff_check_status(ap); - - bfin_irq_clear(ap); -} - -/** * bfin_thaw - Thaw DMA controller port * @ap: port to thaw * @@ -1276,7 +1238,7 @@ void bfin_thaw(struct ata_port *ap) { dev_dbg(ap->dev, "in atapi dma thaw\n"); bfin_check_status(ap); - bfin_irq_on(ap); + ata_sff_irq_on(ap); } /** @@ -1293,7 +1255,7 @@ static void bfin_postreset(struct ata_link *link, unsigned int *classes) void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; /* re-enable interrupts */ - bfin_irq_on(ap); + ata_sff_irq_on(ap); /* is double-select really necessary? */ if (classes[0] != ATA_DEV_NONE) @@ -1438,18 +1400,12 @@ static irqreturn_t bfin_ata_interrupt(int irq, void *dev_instance) spin_lock_irqsave(&host->lock, flags); for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap; + struct ata_port *ap = host->ports[i]; + struct ata_queued_cmd *qc; - ap = host->ports[i]; - if (ap && - !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - - qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) && - (qc->flags & ATA_QCFLAG_ACTIVE)) - handled |= bfin_ata_host_intr(ap, qc); - } + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) + handled |= bfin_ata_host_intr(ap, qc); } spin_unlock_irqrestore(&host->lock, flags); @@ -1465,7 +1421,7 @@ static struct scsi_host_template bfin_sht = { }; static struct ata_port_operations bfin_pata_ops = { - .inherits = &ata_sff_port_ops, + .inherits = &ata_bmdma_port_ops, .set_piomode = bfin_set_piomode, .set_dmamode = bfin_set_dmamode, @@ -1476,6 +1432,7 @@ static struct ata_port_operations bfin_pata_ops = { .sff_check_status = bfin_check_status, .sff_check_altstatus = bfin_check_altstatus, .sff_dev_select = bfin_dev_select, + .sff_set_devctl = bfin_set_devctl, .bmdma_setup = bfin_bmdma_setup, .bmdma_start = bfin_bmdma_start, @@ -1485,13 +1442,11 @@ static struct ata_port_operations bfin_pata_ops = { .qc_prep = ata_noop_qc_prep, - .freeze = bfin_freeze, .thaw = bfin_thaw, .softreset = bfin_softreset, .postreset = bfin_postreset, .sff_irq_clear = bfin_irq_clear, - .sff_irq_on = bfin_irq_on, .port_start = bfin_port_start, .port_stop = bfin_port_stop, diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c index 45896b3c6538..e5f289f59ca3 100644 --- a/drivers/ata/pata_cmd640.c +++ b/drivers/ata/pata_cmd640.c @@ -153,24 +153,20 @@ static int cmd640_port_start(struct ata_port *ap) struct pci_dev *pdev = to_pci_dev(ap->host->dev); struct cmd640_reg *timing; - int ret = ata_sff_port_start(ap); - if (ret < 0) - return ret; - timing = devm_kzalloc(&pdev->dev, sizeof(struct cmd640_reg), GFP_KERNEL); if (timing == NULL) return -ENOMEM; timing->last = -1; /* Force a load */ ap->private_data = timing; - return ret; + return 0; } static struct scsi_host_template cmd640_sht = { - ATA_BMDMA_SHT(DRV_NAME), + ATA_PIO_SHT(DRV_NAME), }; static struct ata_port_operations cmd640_port_ops = { - .inherits = &ata_bmdma_port_ops, + .inherits = &ata_sff_port_ops, /* In theory xfer_noirq is not needed once we kill the prefetcher */ .sff_data_xfer = ata_sff_data_xfer_noirq, .qc_issue = cmd640_qc_issue, @@ -181,13 +177,10 @@ static struct ata_port_operations cmd640_port_ops = { static void cmd640_hardware_init(struct pci_dev *pdev) { - u8 r; u8 ctrl; /* CMD640 detected, commiserations */ pci_write_config_byte(pdev, 0x5B, 0x00); - /* Get version info */ - pci_read_config_byte(pdev, CFR, &r); /* PIO0 command cycles */ pci_write_config_byte(pdev, CMDTIM, 0); /* 512 byte bursts (sector) */ diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c index 95ebdac517f2..17c5f346ff01 100644 --- a/drivers/ata/pata_cs5520.c +++ b/drivers/ata/pata_cs5520.c @@ -110,7 +110,7 @@ static struct scsi_host_template cs5520_sht = { static struct ata_port_operations cs5520_port_ops = { .inherits = &ata_bmdma_port_ops, - .qc_prep = ata_sff_dumb_qc_prep, + .qc_prep = ata_bmdma_dumb_qc_prep, .cable_detect = ata_cable_40wire, .set_piomode = cs5520_set_piomode, }; diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c index 738ad2e14a97..e809a4233a81 100644 --- a/drivers/ata/pata_cs5530.c +++ b/drivers/ata/pata_cs5530.c @@ -156,7 +156,7 @@ static unsigned int cs5530_qc_issue(struct ata_queued_cmd *qc) cs5530_set_dmamode(ap, adev); } - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } static struct scsi_host_template cs5530_sht = { @@ -167,7 +167,7 @@ static struct scsi_host_template cs5530_sht = { static struct ata_port_operations cs5530_port_ops = { .inherits = &ata_bmdma_port_ops, - .qc_prep = ata_sff_dumb_qc_prep, + .qc_prep = ata_bmdma_dumb_qc_prep, .qc_issue = cs5530_qc_issue, .cable_detect = ata_cable_40wire, diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index af49bfb57247..8580eb3cd54d 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -182,7 +182,7 @@ static unsigned long hpt366_filter(struct ata_device *adev, unsigned long mask) } else if (adev->class == ATA_DEV_ATAPI) mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); - return ata_bmdma_mode_filter(adev, mask); + return mask; } static int hpt36x_cable_detect(struct ata_port *ap) diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index 8839307a64cf..98b498b6907c 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -282,7 +282,7 @@ static unsigned long hpt370_filter(struct ata_device *adev, unsigned long mask) if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5)) mask &= ~(0xE0 << ATA_SHIFT_UDMA); } - return ata_bmdma_mode_filter(adev, mask); + return mask; } /** @@ -298,7 +298,7 @@ static unsigned long hpt370a_filter(struct ata_device *adev, unsigned long mask) if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5)) mask &= ~(0xE0 << ATA_SHIFT_UDMA); } - return ata_bmdma_mode_filter(adev, mask); + return mask; } /** diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c index 01457b266f3d..8b95aeba0e74 100644 --- a/drivers/ata/pata_hpt3x2n.c +++ b/drivers/ata/pata_hpt3x2n.c @@ -320,7 +320,7 @@ static unsigned int hpt3x2n_qc_issue(struct ata_queued_cmd *qc) hpt3x2n_set_clock(ap, dpll ? 0x21 : 0x23); } - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } static struct scsi_host_template hpt3x2n_sht = { diff --git a/drivers/ata/pata_icside.c b/drivers/ata/pata_icside.c index fa812e206eeb..b56e8f722d20 100644 --- a/drivers/ata/pata_icside.c +++ b/drivers/ata/pata_icside.c @@ -321,7 +321,7 @@ static void pata_icside_postreset(struct ata_link *link, unsigned int *classes) } static struct ata_port_operations pata_icside_port_ops = { - .inherits = &ata_sff_port_ops, + .inherits = &ata_bmdma_port_ops, /* no need to build any PRD tables for DMA */ .qc_prep = ata_noop_qc_prep, .sff_data_xfer = ata_sff_data_xfer_noirq, @@ -333,7 +333,8 @@ static struct ata_port_operations pata_icside_port_ops = { .cable_detect = ata_cable_40wire, .set_dmamode = pata_icside_set_dmamode, .postreset = pata_icside_postreset, - .post_internal_cmd = pata_icside_bmdma_stop, + + .port_start = ATA_OP_NULL, /* don't need PRD table */ }; static void __devinit diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index 5cb286fd839e..2bd2b002d14a 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -430,7 +430,7 @@ static unsigned int it821x_smart_qc_issue(struct ata_queued_cmd *qc) case 0xFC: /* Internal 'report rebuild state' */ /* Arguably should just no-op this one */ case ATA_CMD_SET_FEATURES: - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } printk(KERN_DEBUG "it821x: can't process command 0x%02X\n", qc->tf.command); return AC_ERR_DEV; @@ -448,7 +448,7 @@ static unsigned int it821x_smart_qc_issue(struct ata_queued_cmd *qc) static unsigned int it821x_passthru_qc_issue(struct ata_queued_cmd *qc) { it821x_passthru_dev_select(qc->ap, qc->dev->devno); - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } /** @@ -739,7 +739,7 @@ static int it821x_port_start(struct ata_port *ap) struct it821x_dev *itdev; u8 conf; - int ret = ata_sff_port_start(ap); + int ret = ata_bmdma_port_start(ap); if (ret < 0) return ret; diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index 211b6438b3a0..25df50f51c04 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -720,6 +720,8 @@ static int pata_macio_port_start(struct ata_port *ap) if (priv->dma_table_cpu == NULL) { dev_err(priv->dev, "Unable to allocate DMA command list\n"); ap->ioaddr.bmdma_addr = NULL; + ap->mwdma_mask = 0; + ap->udma_mask = 0; } return 0; } @@ -917,7 +919,7 @@ static struct scsi_host_template pata_macio_sht = { }; static struct ata_port_operations pata_macio_ops = { - .inherits = &ata_sff_port_ops, + .inherits = &ata_bmdma_port_ops, .freeze = pata_macio_freeze, .set_piomode = pata_macio_set_timings, @@ -925,7 +927,6 @@ static struct ata_port_operations pata_macio_ops = { .cable_detect = pata_macio_cable_detect, .sff_dev_select = pata_macio_dev_select, .qc_prep = pata_macio_qc_prep, - .mode_filter = ata_bmdma_mode_filter, .bmdma_setup = pata_macio_bmdma_setup, .bmdma_start = pata_macio_bmdma_start, .bmdma_stop = pata_macio_bmdma_stop, diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 9f5b053611dd..96b11b604ae0 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -64,13 +64,13 @@ struct mpc52xx_ata_priv { /* ATAPI-4 PIO specs (in ns) */ -static const int ataspec_t0[5] = {600, 383, 240, 180, 120}; -static const int ataspec_t1[5] = { 70, 50, 30, 30, 25}; -static const int ataspec_t2_8[5] = {290, 290, 290, 80, 70}; -static const int ataspec_t2_16[5] = {165, 125, 100, 80, 70}; -static const int ataspec_t2i[5] = { 0, 0, 0, 70, 25}; -static const int ataspec_t4[5] = { 30, 20, 15, 10, 10}; -static const int ataspec_ta[5] = { 35, 35, 35, 35, 35}; +static const u16 ataspec_t0[5] = {600, 383, 240, 180, 120}; +static const u16 ataspec_t1[5] = { 70, 50, 30, 30, 25}; +static const u16 ataspec_t2_8[5] = {290, 290, 290, 80, 70}; +static const u16 ataspec_t2_16[5] = {165, 125, 100, 80, 70}; +static const u16 ataspec_t2i[5] = { 0, 0, 0, 70, 25}; +static const u16 ataspec_t4[5] = { 30, 20, 15, 10, 10}; +static const u16 ataspec_ta[5] = { 35, 35, 35, 35, 35}; #define CALC_CLKCYC(c,v) ((((v)+(c)-1)/(c))) @@ -78,13 +78,13 @@ static const int ataspec_ta[5] = { 35, 35, 35, 35, 35}; /* ATAPI-4 MDMA specs (in clocks) */ struct mdmaspec { - u32 t0M; - u32 td; - u32 th; - u32 tj; - u32 tkw; - u32 tm; - u32 tn; + u8 t0M; + u8 td; + u8 th; + u8 tj; + u8 tkw; + u8 tm; + u8 tn; }; static const struct mdmaspec mdmaspec66[3] = { @@ -101,23 +101,23 @@ static const struct mdmaspec mdmaspec132[3] = { /* ATAPI-4 UDMA specs (in clocks) */ struct udmaspec { - u32 tcyc; - u32 t2cyc; - u32 tds; - u32 tdh; - u32 tdvs; - u32 tdvh; - u32 tfs; - u32 tli; - u32 tmli; - u32 taz; - u32 tzah; - u32 tenv; - u32 tsr; - u32 trfs; - u32 trp; - u32 tack; - u32 tss; + u8 tcyc; + u8 t2cyc; + u8 tds; + u8 tdh; + u8 tdvs; + u8 tdvh; + u8 tfs; + u8 tli; + u8 tmli; + u8 taz; + u8 tzah; + u8 tenv; + u8 tsr; + u8 trfs; + u8 trp; + u8 tack; + u8 tss; }; static const struct udmaspec udmaspec66[6] = { @@ -270,7 +270,7 @@ mpc52xx_ata_compute_pio_timings(struct mpc52xx_ata_priv *priv, int dev, int pio) { struct mpc52xx_ata_timings *timing = &priv->timings[dev]; unsigned int ipb_period = priv->ipb_period; - unsigned int t0, t1, t2_8, t2_16, t2i, t4, ta; + u32 t0, t1, t2_8, t2_16, t2i, t4, ta; if ((pio < 0) || (pio > 4)) return -EINVAL; @@ -299,8 +299,8 @@ mpc52xx_ata_compute_mdma_timings(struct mpc52xx_ata_priv *priv, int dev, if (speed < 0 || speed > 2) return -EINVAL; - t->mdma1 = (s->t0M << 24) | (s->td << 16) | (s->tkw << 8) | (s->tm); - t->mdma2 = (s->th << 24) | (s->tj << 16) | (s->tn << 8); + t->mdma1 = ((u32)s->t0M << 24) | ((u32)s->td << 16) | ((u32)s->tkw << 8) | s->tm; + t->mdma2 = ((u32)s->th << 24) | ((u32)s->tj << 16) | ((u32)s->tn << 8); t->using_udma = 0; return 0; @@ -316,11 +316,11 @@ mpc52xx_ata_compute_udma_timings(struct mpc52xx_ata_priv *priv, int dev, if (speed < 0 || speed > 2) return -EINVAL; - t->udma1 = (s->t2cyc << 24) | (s->tcyc << 16) | (s->tds << 8) | s->tdh; - t->udma2 = (s->tdvs << 24) | (s->tdvh << 16) | (s->tfs << 8) | s->tli; - t->udma3 = (s->tmli << 24) | (s->taz << 16) | (s->tenv << 8) | s->tsr; - t->udma4 = (s->tss << 24) | (s->trfs << 16) | (s->trp << 8) | s->tack; - t->udma5 = (s->tzah << 24); + t->udma1 = ((u32)s->t2cyc << 24) | ((u32)s->tcyc << 16) | ((u32)s->tds << 8) | s->tdh; + t->udma2 = ((u32)s->tdvs << 24) | ((u32)s->tdvh << 16) | ((u32)s->tfs << 8) | s->tli; + t->udma3 = ((u32)s->tmli << 24) | ((u32)s->taz << 16) | ((u32)s->tenv << 8) | s->tsr; + t->udma4 = ((u32)s->tss << 24) | ((u32)s->trfs << 16) | ((u32)s->trp << 8) | s->tack; + t->udma5 = (u32)s->tzah << 24; t->using_udma = 1; return 0; diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c index 830431f036a1..fdbba2d76d3e 100644 --- a/drivers/ata/pata_ns87415.c +++ b/drivers/ata/pata_ns87415.c @@ -126,7 +126,7 @@ static void ns87415_bmdma_setup(struct ata_queued_cmd *qc) /* load PRD table addr. */ mb(); /* make sure PRD table writes are visible to controller */ - iowrite32(ap->prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS); + iowrite32(ap->bmdma_prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS); /* specify data direction, triple-check start bit is clear */ dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c index 005a44483a7b..3001109352ea 100644 --- a/drivers/ata/pata_octeon_cf.c +++ b/drivers/ata/pata_octeon_cf.c @@ -489,9 +489,8 @@ static void octeon_cf_exec_command16(struct ata_port *ap, ata_wait_idle(ap); } -static u8 octeon_cf_irq_on(struct ata_port *ap) +static void octeon_cf_irq_on(struct ata_port *ap) { - return 0; } static void octeon_cf_irq_clear(struct ata_port *ap) @@ -655,9 +654,6 @@ static irqreturn_t octeon_cf_interrupt(int irq, void *dev_instance) ap = host->ports[i]; ocd = ap->dev->platform_data; - if (ap->flags & ATA_FLAG_DISABLED) - continue; - ocd = ap->dev->platform_data; cf_port = ap->private_data; dma_int.u64 = @@ -667,8 +663,7 @@ static irqreturn_t octeon_cf_interrupt(int irq, void *dev_instance) qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) && - (qc->flags & ATA_QCFLAG_ACTIVE)) { + if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) { if (dma_int.s.done && !dma_cfg.s.en) { if (!sg_is_last(qc->cursg)) { qc->cursg = sg_next(qc->cursg); @@ -738,8 +733,7 @@ static void octeon_cf_delayed_finish(struct work_struct *work) goto out; } qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) && - (qc->flags & ATA_QCFLAG_ACTIVE)) + if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) octeon_cf_dma_finished(ap, qc); out: spin_unlock_irqrestore(&host->lock, flags); diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index 5f6aba7eb0dd..988ef2627be3 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -200,7 +200,7 @@ static unsigned int oldpiix_qc_issue(struct ata_queued_cmd *qc) if (ata_dma_enabled(adev)) oldpiix_set_dmamode(ap, adev); } - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index d94b8f0bd743..118c28e8abaf 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -45,16 +45,6 @@ #define DRV_NAME "pata_pcmcia" #define DRV_VERSION "0.3.5" -/* - * Private data structure to glue stuff together - */ - -struct ata_pcmcia_info { - struct pcmcia_device *pdev; - int ndev; - dev_node_t node; -}; - /** * pcmcia_set_mode - PCMCIA specific mode setup * @link: link @@ -175,7 +165,7 @@ static struct ata_port_operations pcmcia_8bit_port_ops = { .sff_data_xfer = ata_data_xfer_8bit, .cable_detect = ata_cable_40wire, .set_mode = pcmcia_set_mode_8bit, - .drain_fifo = pcmcia_8bit_drain_fifo, + .sff_drain_fifo = pcmcia_8bit_drain_fifo, }; @@ -248,7 +238,6 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) { struct ata_host *host; struct ata_port *ap; - struct ata_pcmcia_info *info; struct pcmcia_config_check *stk = NULL; int is_kme = 0, ret = -ENOMEM, p; unsigned long io_base, ctl_base; @@ -256,19 +245,10 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) int n_ports = 1; struct ata_port_operations *ops = &pcmcia_port_ops; - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (info == NULL) - return -ENOMEM; - - /* Glue stuff together. FIXME: We may be able to get rid of info with care */ - info->pdev = pdev; - pdev->priv = info; - /* Set up attributes in order to probe card and get resources */ pdev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; pdev->io.Attributes2 = IO_DATA_PATH_WIDTH_8; pdev->io.IOAddrLines = 3; - pdev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; pdev->conf.Attributes = CONF_ENABLE_IRQ; pdev->conf.IntType = INT_MEMORY_AND_IO; @@ -293,8 +273,7 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) } io_base = pdev->io.BasePort1; ctl_base = stk->ctl_base; - ret = pcmcia_request_irq(pdev, &pdev->irq); - if (ret) + if (!pdev->irq) goto failed; ret = pcmcia_request_configuration(pdev, &pdev->conf); @@ -344,21 +323,19 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) } /* activate */ - ret = ata_host_activate(host, pdev->irq.AssignedIRQ, ata_sff_interrupt, + ret = ata_host_activate(host, pdev->irq, ata_sff_interrupt, IRQF_SHARED, &pcmcia_sht); if (ret) goto failed; - info->ndev = 1; + pdev->priv = host; kfree(stk); return 0; failed: kfree(stk); - info->ndev = 0; pcmcia_disable_device(pdev); out1: - kfree(info); return ret; } @@ -372,20 +349,12 @@ out1: static void pcmcia_remove_one(struct pcmcia_device *pdev) { - struct ata_pcmcia_info *info = pdev->priv; - struct device *dev = &pdev->dev; - - if (info != NULL) { - /* If we have attached the device to the ATA layer, detach it */ - if (info->ndev) { - struct ata_host *host = dev_get_drvdata(dev); - ata_host_detach(host); - } - info->ndev = 0; - pdev->priv = NULL; - } + struct ata_host *host = pdev->priv; + + if (host) + ata_host_detach(host); + pcmcia_disable_device(pdev); - kfree(info); } static struct pcmcia_device_id pcmcia_devices[] = { diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index ca5cad0fd80b..09f1f22c0307 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -265,7 +265,7 @@ static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long struct ata_device *pair = ata_dev_pair(adev); if (adev->class != ATA_DEV_ATA || adev->devno == 0 || pair == NULL) - return ata_bmdma_mode_filter(adev, mask); + return mask; /* Check for slave of a Maxtor at UDMA6 */ ata_id_c_string(pair->id, model_num, ATA_ID_PROD, @@ -274,7 +274,7 @@ static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long if (strstr(model_num, "Maxtor") == NULL && pair->dma_mode == XFER_UDMA_6) mask &= ~ (1 << (6 + ATA_SHIFT_UDMA)); - return ata_bmdma_mode_filter(adev, mask); + return mask; } /** diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c index 9ac0897cf8b0..fa1e2f3bc0fd 100644 --- a/drivers/ata/pata_pdc202xx_old.c +++ b/drivers/ata/pata_pdc202xx_old.c @@ -249,7 +249,7 @@ static int pdc2026x_port_start(struct ata_port *ap) u8 burst = ioread8(bmdma + 0x1f); iowrite8(burst | 0x01, bmdma + 0x1f); } - return ata_sff_port_start(ap); + return ata_bmdma_port_start(ap); } /** diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 3f6ebc6c665a..50400fa120fe 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -53,7 +53,6 @@ static struct ata_port_operations pata_platform_port_ops = { .sff_data_xfer = ata_sff_data_xfer_noirq, .cable_detect = ata_cable_unknown, .set_mode = pata_platform_set_mode, - .port_start = ATA_OP_NULL, }; static void pata_platform_setup_port(struct ata_ioports *ioaddr, diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c index fc9602229acb..a5fa388e5398 100644 --- a/drivers/ata/pata_radisys.c +++ b/drivers/ata/pata_radisys.c @@ -179,7 +179,7 @@ static unsigned int radisys_qc_issue(struct ata_queued_cmd *qc) radisys_set_piomode(ap, adev); } } - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c index dfecc6f964b0..6b5b63a2fd8e 100644 --- a/drivers/ata/pata_sc1200.c +++ b/drivers/ata/pata_sc1200.c @@ -174,7 +174,7 @@ static unsigned int sc1200_qc_issue(struct ata_queued_cmd *qc) sc1200_set_dmamode(ap, adev); } - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } /** @@ -209,7 +209,7 @@ static struct scsi_host_template sc1200_sht = { static struct ata_port_operations sc1200_port_ops = { .inherits = &ata_bmdma_port_ops, - .qc_prep = ata_sff_dumb_qc_prep, + .qc_prep = ata_bmdma_dumb_qc_prep, .qc_issue = sc1200_qc_issue, .qc_defer = sc1200_qc_defer, .cable_detect = ata_cable_40wire, diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index 4257d6b40af4..6f6193b707cb 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -265,7 +265,7 @@ unsigned long scc_mode_filter(struct ata_device *adev, unsigned long mask) printk(KERN_INFO "%s: limit ATAPI UDMA to UDMA4\n", DRV_NAME); mask &= ~(0xE0 << ATA_SHIFT_UDMA); } - return ata_bmdma_mode_filter(adev, mask); + return mask; } /** @@ -416,6 +416,17 @@ static void scc_dev_select (struct ata_port *ap, unsigned int device) } /** + * scc_set_devctl - Write device control reg + * @ap: port where the device is + * @ctl: value to write + */ + +static void scc_set_devctl(struct ata_port *ap, u8 ctl) +{ + out_be32(ap->ioaddr.ctl_addr, ctl); +} + +/** * scc_bmdma_setup - Set up PCI IDE BMDMA transaction * @qc: Info associated with this ATA transaction. * @@ -430,7 +441,7 @@ static void scc_bmdma_setup (struct ata_queued_cmd *qc) void __iomem *mmio = ap->ioaddr.bmdma_addr; /* load PRD table addr */ - out_be32(mmio + SCC_DMA_TABLE_OFS, ap->prd_dma); + out_be32(mmio + SCC_DMA_TABLE_OFS, ap->bmdma_prd_dma); /* specify data direction, triple-check start bit is clear */ dmactl = in_be32(mmio + SCC_DMA_CMD); @@ -501,8 +512,8 @@ static unsigned int scc_devchk (struct ata_port *ap, * Note: Original code is ata_sff_wait_after_reset */ -int scc_wait_after_reset(struct ata_link *link, unsigned int devmask, - unsigned long deadline) +static int scc_wait_after_reset(struct ata_link *link, unsigned int devmask, + unsigned long deadline) { struct ata_port *ap = link->ap; struct ata_ioports *ioaddr = &ap->ioaddr; @@ -817,54 +828,6 @@ static unsigned int scc_data_xfer (struct ata_device *dev, unsigned char *buf, } /** - * scc_irq_on - Enable interrupts on a port. - * @ap: Port on which interrupts are enabled. - * - * Note: Original code is ata_sff_irq_on(). - */ - -static u8 scc_irq_on (struct ata_port *ap) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - u8 tmp; - - ap->ctl &= ~ATA_NIEN; - ap->last_ctl = ap->ctl; - - out_be32(ioaddr->ctl_addr, ap->ctl); - tmp = ata_wait_idle(ap); - - ap->ops->sff_irq_clear(ap); - - return tmp; -} - -/** - * scc_freeze - Freeze BMDMA controller port - * @ap: port to freeze - * - * Note: Original code is ata_sff_freeze(). - */ - -static void scc_freeze (struct ata_port *ap) -{ - struct ata_ioports *ioaddr = &ap->ioaddr; - - ap->ctl |= ATA_NIEN; - ap->last_ctl = ap->ctl; - - out_be32(ioaddr->ctl_addr, ap->ctl); - - /* Under certain circumstances, some controllers raise IRQ on - * ATA_NIEN manipulation. Also, many controllers fail to mask - * previously pending IRQ on ATA_NIEN assertion. Clear it. - */ - ap->ops->sff_check_status(ap); - - ap->ops->sff_irq_clear(ap); -} - -/** * scc_pata_prereset - prepare for reset * @ap: ATA port to be reset * @deadline: deadline jiffies for the operation @@ -903,8 +866,7 @@ static void scc_postreset(struct ata_link *link, unsigned int *classes) } /* set up device control */ - if (ap->ioaddr.ctl_addr) - out_be32(ap->ioaddr.ctl_addr, ap->ctl); + out_be32(ap->ioaddr.ctl_addr, ap->ctl); DPRINTK("EXIT\n"); } @@ -930,7 +892,7 @@ static void scc_irq_clear (struct ata_port *ap) * scc_port_start - Set port up for dma. * @ap: Port to initialize * - * Allocate space for PRD table using ata_port_start(). + * Allocate space for PRD table using ata_bmdma_port_start(). * Set PRD table address for PTERADD. (PRD Transfer End Read) */ @@ -939,11 +901,11 @@ static int scc_port_start (struct ata_port *ap) void __iomem *mmio = ap->ioaddr.bmdma_addr; int rc; - rc = ata_port_start(ap); + rc = ata_bmdma_port_start(ap); if (rc) return rc; - out_be32(mmio + SCC_DMA_PTERADD, ap->prd_dma); + out_be32(mmio + SCC_DMA_PTERADD, ap->bmdma_prd_dma); return 0; } @@ -978,6 +940,7 @@ static struct ata_port_operations scc_pata_ops = { .sff_check_status = scc_check_status, .sff_check_altstatus = scc_check_altstatus, .sff_dev_select = scc_dev_select, + .sff_set_devctl = scc_set_devctl, .bmdma_setup = scc_bmdma_setup, .bmdma_start = scc_bmdma_start, @@ -985,14 +948,11 @@ static struct ata_port_operations scc_pata_ops = { .bmdma_status = scc_bmdma_status, .sff_data_xfer = scc_data_xfer, - .freeze = scc_freeze, .prereset = scc_pata_prereset, .softreset = scc_softreset, .postreset = scc_postreset, - .post_internal_cmd = scc_bmdma_stop, .sff_irq_clear = scc_irq_clear, - .sff_irq_on = scc_irq_on, .port_start = scc_port_start, .port_stop = scc_port_stop, diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c index 99cceb458e2a..86b3d0133c7c 100644 --- a/drivers/ata/pata_sch.c +++ b/drivers/ata/pata_sch.c @@ -174,22 +174,12 @@ static int __devinit sch_init_one(struct pci_dev *pdev, { static int printed_version; const struct ata_port_info *ppi[] = { &sch_port_info, NULL }; - struct ata_host *host; - int rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - /* enable device and prepare host */ - rc = pcim_enable_device(pdev); - if (rc) - return rc; - rc = ata_pci_sff_prepare_host(pdev, ppi, &host); - if (rc) - return rc; - pci_set_master(pdev); - return ata_pci_sff_activate_host(host, ata_sff_interrupt, &sch_sht); + return ata_pci_sff_init_one(pdev, ppi, &sch_sht, NULL, 0); } static int __init sch_init(void) diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c index 9524d54035f7..43ea389df2b3 100644 --- a/drivers/ata/pata_serverworks.c +++ b/drivers/ata/pata_serverworks.c @@ -198,7 +198,7 @@ static unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned l { if (adev->class == ATA_DEV_ATA) mask &= ~ATA_MASK_UDMA; - return ata_bmdma_mode_filter(adev, mask); + return mask; } @@ -218,7 +218,7 @@ static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned lo /* Disk, UDMA */ if (adev->class != ATA_DEV_ATA) - return ata_bmdma_mode_filter(adev, mask); + return mask; /* Actually do need to check */ ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); @@ -227,7 +227,7 @@ static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned lo if (!strcmp(p, model_num)) mask &= ~(0xE0 << ATA_SHIFT_UDMA); } - return ata_bmdma_mode_filter(adev, mask); + return mask; } /** diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c index c6c589c23ffc..43faf106f647 100644 --- a/drivers/ata/pata_sil680.c +++ b/drivers/ata/pata_sil680.c @@ -190,15 +190,37 @@ static void sil680_set_dmamode(struct ata_port *ap, struct ata_device *adev) pci_write_config_word(pdev, ua, ultra); } +/** + * sil680_sff_exec_command - issue ATA command to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Issues ATA command, with proper synchronization with interrupt + * handler / other threads. Use our MMIO space for PCI posting to avoid + * a hideously slow cycle all the way to the device. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +void sil680_sff_exec_command(struct ata_port *ap, + const struct ata_taskfile *tf) +{ + DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command); + iowrite8(tf->command, ap->ioaddr.command_addr); + ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); +} + static struct scsi_host_template sil680_sht = { ATA_BMDMA_SHT(DRV_NAME), }; + static struct ata_port_operations sil680_port_ops = { - .inherits = &ata_bmdma32_port_ops, - .cable_detect = sil680_cable_detect, - .set_piomode = sil680_set_piomode, - .set_dmamode = sil680_set_dmamode, + .inherits = &ata_bmdma32_port_ops, + .sff_exec_command = sil680_sff_exec_command, + .cable_detect = sil680_cable_detect, + .set_piomode = sil680_set_piomode, + .set_dmamode = sil680_set_dmamode, }; /** diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 741e7cb69d8c..7e3e0a5598b7 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -355,7 +355,7 @@ static unsigned long via_mode_filter(struct ata_device *dev, unsigned long mask) mask &= ~ ATA_MASK_UDMA; } } - return ata_bmdma_mode_filter(dev, mask); + return mask; } /** @@ -417,8 +417,6 @@ static void via_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) tf->lbam, tf->lbah); } - - ata_wait_idle(ap); } static int via_port_start(struct ata_port *ap) @@ -426,7 +424,7 @@ static int via_port_start(struct ata_port *ap) struct via_port *vp; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - int ret = ata_sff_port_start(ap); + int ret = ata_bmdma_port_start(ap); if (ret < 0) return ret; diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 5904cfdb8dbe..adbe0426c8f0 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -324,10 +324,8 @@ static void adma_qc_prep(struct ata_queued_cmd *qc) VPRINTK("ENTER\n"); adma_enter_reg_mode(qc->ap); - if (qc->tf.protocol != ATA_PROT_DMA) { - ata_sff_qc_prep(qc); + if (qc->tf.protocol != ATA_PROT_DMA) return; - } buf[i++] = 0; /* Response flags */ buf[i++] = 0; /* reserved */ @@ -442,8 +440,6 @@ static inline unsigned int adma_intr_pkt(struct ata_host *host) continue; handled = 1; adma_enter_reg_mode(ap); - if (ap->flags & ATA_FLAG_DISABLED) - continue; pp = ap->private_data; if (!pp || pp->state != adma_state_pkt) continue; @@ -484,42 +480,38 @@ static inline unsigned int adma_intr_mmio(struct ata_host *host) unsigned int handled = 0, port_no; for (port_no = 0; port_no < host->n_ports; ++port_no) { - struct ata_port *ap; - ap = host->ports[port_no]; - if (ap && (!(ap->flags & ATA_FLAG_DISABLED))) { - struct ata_queued_cmd *qc; - struct adma_port_priv *pp = ap->private_data; - if (!pp || pp->state != adma_state_mmio) + struct ata_port *ap = host->ports[port_no]; + struct adma_port_priv *pp = ap->private_data; + struct ata_queued_cmd *qc; + + if (!pp || pp->state != adma_state_mmio) + continue; + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { + + /* check main status, clearing INTRQ */ + u8 status = ata_sff_check_status(ap); + if ((status & ATA_BUSY)) continue; - qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { - - /* check main status, clearing INTRQ */ - u8 status = ata_sff_check_status(ap); - if ((status & ATA_BUSY)) - continue; - DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", - ap->print_id, qc->tf.protocol, status); - - /* complete taskfile transaction */ - pp->state = adma_state_idle; - qc->err_mask |= ac_err_mask(status); - if (!qc->err_mask) - ata_qc_complete(qc); - else { - struct ata_eh_info *ehi = - &ap->link.eh_info; - ata_ehi_clear_desc(ehi); - ata_ehi_push_desc(ehi, - "status 0x%02X", status); - - if (qc->err_mask == AC_ERR_DEV) - ata_port_abort(ap); - else - ata_port_freeze(ap); - } - handled = 1; + DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", + ap->print_id, qc->tf.protocol, status); + + /* complete taskfile transaction */ + pp->state = adma_state_idle; + qc->err_mask |= ac_err_mask(status); + if (!qc->err_mask) + ata_qc_complete(qc); + else { + struct ata_eh_info *ehi = &ap->link.eh_info; + ata_ehi_clear_desc(ehi); + ata_ehi_push_desc(ehi, "status 0x%02X", status); + + if (qc->err_mask == AC_ERR_DEV) + ata_port_abort(ap); + else + ata_port_freeze(ap); } + handled = 1; } } return handled; @@ -562,11 +554,7 @@ static int adma_port_start(struct ata_port *ap) { struct device *dev = ap->host->dev; struct adma_port_priv *pp; - int rc; - rc = ata_port_start(ap); - if (rc) - return rc; adma_enter_reg_mode(ap); pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); if (!pp) diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 27dc6c86a4cd..a36149ebf4a2 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -415,22 +415,11 @@ static irqreturn_t inic_interrupt(int irq, void *dev_instance) spin_lock(&host->lock); - for (i = 0; i < NR_PORTS; i++) { - struct ata_port *ap = host->ports[i]; - - if (!(host_irq_stat & (HIRQ_PORT0 << i))) - continue; - - if (likely(ap && !(ap->flags & ATA_FLAG_DISABLED))) { - inic_host_intr(ap); + for (i = 0; i < NR_PORTS; i++) + if (host_irq_stat & (HIRQ_PORT0 << i)) { + inic_host_intr(host->ports[i]); handled++; - } else { - if (ata_ratelimit()) - dev_printk(KERN_ERR, host->dev, "interrupt " - "from disabled port %d (0x%x)\n", - i, host_irq_stat); } - } spin_unlock(&host->lock); @@ -679,8 +668,7 @@ static void init_port(struct ata_port *ap) memset(pp->pkt, 0, sizeof(struct inic_pkt)); memset(pp->cpb_tbl, 0, IDMA_CPB_TBL_SIZE); - /* setup PRD and CPB lookup table addresses */ - writel(ap->prd_dma, port_base + PORT_PRD_ADDR); + /* setup CPB lookup table addresses */ writel(pp->cpb_tbl_dma, port_base + PORT_CPB_CPBLAR); } @@ -694,7 +682,6 @@ static int inic_port_start(struct ata_port *ap) { struct device *dev = ap->host->dev; struct inic_port_priv *pp; - int rc; /* alloc and initialize private data */ pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); @@ -703,10 +690,6 @@ static int inic_port_start(struct ata_port *ap) ap->private_data = pp; /* Alloc resources */ - rc = ata_port_start(ap); - if (rc) - return rc; - pp->pkt = dmam_alloc_coherent(dev, sizeof(struct inic_pkt), &pp->pkt_dma, GFP_KERNEL); if (!pp->pkt) diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 71cc0d42f9e1..f3471bc949d3 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -686,16 +686,27 @@ static struct ata_port_operations mv5_ops = { }; static struct ata_port_operations mv6_ops = { - .inherits = &mv5_ops, + .inherits = &ata_bmdma_port_ops, + + .lost_interrupt = ATA_OP_NULL, + + .qc_defer = mv_qc_defer, + .qc_prep = mv_qc_prep, + .qc_issue = mv_qc_issue, + .dev_config = mv6_dev_config, - .scr_read = mv_scr_read, - .scr_write = mv_scr_write, + .freeze = mv_eh_freeze, + .thaw = mv_eh_thaw, + .hardreset = mv_hardreset, + .softreset = mv_softreset, .pmp_hardreset = mv_pmp_hardreset, .pmp_softreset = mv_softreset, - .softreset = mv_softreset, .error_handler = mv_pmp_error_handler, + .scr_read = mv_scr_read, + .scr_write = mv_scr_write, + .sff_check_status = mv_sff_check_status, .sff_irq_clear = mv_sff_irq_clear, .check_atapi_dma = mv_check_atapi_dma, @@ -703,6 +714,9 @@ static struct ata_port_operations mv6_ops = { .bmdma_start = mv_bmdma_start, .bmdma_stop = mv_bmdma_stop, .bmdma_status = mv_bmdma_status, + + .port_start = mv_port_start, + .port_stop = mv_port_stop, }; static struct ata_port_operations mv_iie_ops = { @@ -2248,7 +2262,7 @@ static unsigned int mv_qc_issue_fis(struct ata_queued_cmd *qc) } if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); return 0; } @@ -2344,7 +2358,7 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc) if (IS_GEN_II(hpriv)) return mv_qc_issue_fis(qc); } - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } static struct ata_queued_cmd *mv_get_active_qc(struct ata_port *ap) @@ -2355,13 +2369,9 @@ static struct ata_queued_cmd *mv_get_active_qc(struct ata_port *ap) if (pp->pp_flags & MV_PP_FLAG_NCQ_EN) return NULL; qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc) { - if (qc->tf.flags & ATA_TFLAG_POLLING) - qc = NULL; - else if (!(qc->flags & ATA_QCFLAG_ACTIVE)) - qc = NULL; - } - return qc; + if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) + return qc; + return NULL; } static void mv_pmp_error_handler(struct ata_port *ap) @@ -2546,9 +2556,7 @@ static void mv_unexpected_intr(struct ata_port *ap, int edma_was_enabled) char *when = "idle"; ata_ehi_clear_desc(ehi); - if (ap->flags & ATA_FLAG_DISABLED) { - when = "disabled"; - } else if (edma_was_enabled) { + if (edma_was_enabled) { when = "EDMA enabled"; } else { struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->link.active_tag); @@ -2782,10 +2790,6 @@ static void mv_port_intr(struct ata_port *ap, u32 port_cause) struct mv_port_priv *pp; int edma_was_enabled; - if (ap->flags & ATA_FLAG_DISABLED) { - mv_unexpected_intr(ap, 0); - return; - } /* * Grab a snapshot of the EDMA_EN flag setting, * so that we have a consistent view for this port, @@ -3656,9 +3660,6 @@ static void mv_port_init(struct ata_ioports *port, void __iomem *port_mmio) /* special case: control/altstatus doesn't have ATA_REG_ address */ port->altstatus_addr = port->ctl_addr = shd_base + SHD_CTL_AST; - /* unused: */ - port->cmd_addr = port->bmdma_addr = port->scr_addr = NULL; - /* Clear any currently outstanding port interrupt conditions */ serr = port_mmio + mv_scr_offset(SCR_ERROR); writelfl(readl(serr), serr); diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 2a98b09ab735..baa8f0d2c86f 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -272,7 +272,7 @@ enum ncq_saw_flag_list { }; struct nv_swncq_port_priv { - struct ata_prd *prd; /* our SG list */ + struct ata_bmdma_prd *prd; /* our SG list */ dma_addr_t prd_dma; /* and its DMA mapping */ void __iomem *sactive_block; void __iomem *irq_block; @@ -933,107 +933,108 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance) for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; + struct nv_adma_port_priv *pp = ap->private_data; + void __iomem *mmio = pp->ctl_block; + u16 status; + u32 gen_ctl; + u32 notifier, notifier_error; + notifier_clears[i] = 0; - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - struct nv_adma_port_priv *pp = ap->private_data; - void __iomem *mmio = pp->ctl_block; - u16 status; - u32 gen_ctl; - u32 notifier, notifier_error; - - /* if ADMA is disabled, use standard ata interrupt handler */ - if (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) { - u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804) - >> (NV_INT_PORT_SHIFT * i); - handled += nv_host_intr(ap, irq_stat); - continue; - } + /* if ADMA is disabled, use standard ata interrupt handler */ + if (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) { + u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804) + >> (NV_INT_PORT_SHIFT * i); + handled += nv_host_intr(ap, irq_stat); + continue; + } - /* if in ATA register mode, check for standard interrupts */ - if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) { - u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804) - >> (NV_INT_PORT_SHIFT * i); - if (ata_tag_valid(ap->link.active_tag)) - /** NV_INT_DEV indication seems unreliable at times - at least in ADMA mode. Force it on always when a - command is active, to prevent losing interrupts. */ - irq_stat |= NV_INT_DEV; - handled += nv_host_intr(ap, irq_stat); - } + /* if in ATA register mode, check for standard interrupts */ + if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) { + u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804) + >> (NV_INT_PORT_SHIFT * i); + if (ata_tag_valid(ap->link.active_tag)) + /** NV_INT_DEV indication seems unreliable + at times at least in ADMA mode. Force it + on always when a command is active, to + prevent losing interrupts. */ + irq_stat |= NV_INT_DEV; + handled += nv_host_intr(ap, irq_stat); + } + + notifier = readl(mmio + NV_ADMA_NOTIFIER); + notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR); + notifier_clears[i] = notifier | notifier_error; + + gen_ctl = readl(pp->gen_block + NV_ADMA_GEN_CTL); + + if (!NV_ADMA_CHECK_INTR(gen_ctl, ap->port_no) && !notifier && + !notifier_error) + /* Nothing to do */ + continue; + + status = readw(mmio + NV_ADMA_STAT); + + /* + * Clear status. Ensure the controller sees the + * clearing before we start looking at any of the CPB + * statuses, so that any CPB completions after this + * point in the handler will raise another interrupt. + */ + writew(status, mmio + NV_ADMA_STAT); + readw(mmio + NV_ADMA_STAT); /* flush posted write */ + rmb(); + + handled++; /* irq handled if we got here */ - notifier = readl(mmio + NV_ADMA_NOTIFIER); - notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR); - notifier_clears[i] = notifier | notifier_error; - - gen_ctl = readl(pp->gen_block + NV_ADMA_GEN_CTL); - - if (!NV_ADMA_CHECK_INTR(gen_ctl, ap->port_no) && !notifier && - !notifier_error) - /* Nothing to do */ - continue; - - status = readw(mmio + NV_ADMA_STAT); - - /* Clear status. Ensure the controller sees the clearing before we start - looking at any of the CPB statuses, so that any CPB completions after - this point in the handler will raise another interrupt. */ - writew(status, mmio + NV_ADMA_STAT); - readw(mmio + NV_ADMA_STAT); /* flush posted write */ - rmb(); - - handled++; /* irq handled if we got here */ - - /* freeze if hotplugged or controller error */ - if (unlikely(status & (NV_ADMA_STAT_HOTPLUG | - NV_ADMA_STAT_HOTUNPLUG | - NV_ADMA_STAT_TIMEOUT | - NV_ADMA_STAT_SERROR))) { - struct ata_eh_info *ehi = &ap->link.eh_info; - - ata_ehi_clear_desc(ehi); - __ata_ehi_push_desc(ehi, "ADMA status 0x%08x: ", status); - if (status & NV_ADMA_STAT_TIMEOUT) { - ehi->err_mask |= AC_ERR_SYSTEM; - ata_ehi_push_desc(ehi, "timeout"); - } else if (status & NV_ADMA_STAT_HOTPLUG) { - ata_ehi_hotplugged(ehi); - ata_ehi_push_desc(ehi, "hotplug"); - } else if (status & NV_ADMA_STAT_HOTUNPLUG) { - ata_ehi_hotplugged(ehi); - ata_ehi_push_desc(ehi, "hot unplug"); - } else if (status & NV_ADMA_STAT_SERROR) { - /* let libata analyze SError and figure out the cause */ - ata_ehi_push_desc(ehi, "SError"); - } else - ata_ehi_push_desc(ehi, "unknown"); - ata_port_freeze(ap); - continue; + /* freeze if hotplugged or controller error */ + if (unlikely(status & (NV_ADMA_STAT_HOTPLUG | + NV_ADMA_STAT_HOTUNPLUG | + NV_ADMA_STAT_TIMEOUT | + NV_ADMA_STAT_SERROR))) { + struct ata_eh_info *ehi = &ap->link.eh_info; + + ata_ehi_clear_desc(ehi); + __ata_ehi_push_desc(ehi, "ADMA status 0x%08x: ", status); + if (status & NV_ADMA_STAT_TIMEOUT) { + ehi->err_mask |= AC_ERR_SYSTEM; + ata_ehi_push_desc(ehi, "timeout"); + } else if (status & NV_ADMA_STAT_HOTPLUG) { + ata_ehi_hotplugged(ehi); + ata_ehi_push_desc(ehi, "hotplug"); + } else if (status & NV_ADMA_STAT_HOTUNPLUG) { + ata_ehi_hotplugged(ehi); + ata_ehi_push_desc(ehi, "hot unplug"); + } else if (status & NV_ADMA_STAT_SERROR) { + /* let EH analyze SError and figure out cause */ + ata_ehi_push_desc(ehi, "SError"); + } else + ata_ehi_push_desc(ehi, "unknown"); + ata_port_freeze(ap); + continue; + } + + if (status & (NV_ADMA_STAT_DONE | + NV_ADMA_STAT_CPBERR | + NV_ADMA_STAT_CMD_COMPLETE)) { + u32 check_commands = notifier_clears[i]; + int pos, error = 0; + + if (status & NV_ADMA_STAT_CPBERR) { + /* check all active commands */ + if (ata_tag_valid(ap->link.active_tag)) + check_commands = 1 << + ap->link.active_tag; + else + check_commands = ap->link.sactive; } - if (status & (NV_ADMA_STAT_DONE | - NV_ADMA_STAT_CPBERR | - NV_ADMA_STAT_CMD_COMPLETE)) { - u32 check_commands = notifier_clears[i]; - int pos, error = 0; - - if (status & NV_ADMA_STAT_CPBERR) { - /* Check all active commands */ - if (ata_tag_valid(ap->link.active_tag)) - check_commands = 1 << - ap->link.active_tag; - else - check_commands = ap-> - link.sactive; - } - - /** Check CPBs for completed commands */ - while ((pos = ffs(check_commands)) && !error) { - pos--; - error = nv_adma_check_cpb(ap, pos, + /* check CPBs for completed commands */ + while ((pos = ffs(check_commands)) && !error) { + pos--; + error = nv_adma_check_cpb(ap, pos, notifier_error & (1 << pos)); - check_commands &= ~(1 << pos); - } + check_commands &= ~(1 << pos); } } } @@ -1130,7 +1131,7 @@ static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc) struct nv_adma_port_priv *pp = qc->ap->private_data; if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) - ata_sff_post_internal_cmd(qc); + ata_bmdma_post_internal_cmd(qc); } static int nv_adma_port_start(struct ata_port *ap) @@ -1155,7 +1156,8 @@ static int nv_adma_port_start(struct ata_port *ap) if (rc) return rc; - rc = ata_port_start(ap); + /* we might fallback to bmdma, allocate bmdma resources */ + rc = ata_bmdma_port_start(ap); if (rc) return rc; @@ -1407,7 +1409,7 @@ static void nv_adma_qc_prep(struct ata_queued_cmd *qc) BUG_ON(!(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) && (qc->flags & ATA_QCFLAG_DMAMAP)); nv_adma_register_mode(qc->ap); - ata_sff_qc_prep(qc); + ata_bmdma_qc_prep(qc); return; } @@ -1466,7 +1468,7 @@ static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc) BUG_ON(!(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) && (qc->flags & ATA_QCFLAG_DMAMAP)); nv_adma_register_mode(qc->ap); - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); } else nv_adma_mode(qc->ap); @@ -1498,22 +1500,19 @@ static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance) spin_lock_irqsave(&host->lock, flags); for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap; - - ap = host->ports[i]; - if (ap && - !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; + struct ata_port *ap = host->ports[i]; + struct ata_queued_cmd *qc; - qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) - handled += ata_sff_host_intr(ap, qc); - else - // No request pending? Clear interrupt status - // anyway, in case there's one pending. - ap->ops->sff_check_status(ap); + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { + handled += ata_sff_host_intr(ap, qc); + } else { + /* + * No request pending? Clear interrupt status + * anyway, in case there's one pending. + */ + ap->ops->sff_check_status(ap); } - } spin_unlock_irqrestore(&host->lock, flags); @@ -1526,11 +1525,7 @@ static irqreturn_t nv_do_interrupt(struct ata_host *host, u8 irq_stat) int i, handled = 0; for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) - handled += nv_host_intr(ap, irq_stat); - + handled += nv_host_intr(host->ports[i], irq_stat); irq_stat >>= NV_INT_PORT_SHIFT; } @@ -1744,7 +1739,7 @@ static void nv_adma_error_handler(struct ata_port *ap) readw(mmio + NV_ADMA_CTL); /* flush posted write */ } - ata_sff_error_handler(ap); + ata_bmdma_error_handler(ap); } static void nv_swncq_qc_to_dq(struct ata_port *ap, struct ata_queued_cmd *qc) @@ -1870,7 +1865,7 @@ static void nv_swncq_error_handler(struct ata_port *ap) ehc->i.action |= ATA_EH_RESET; } - ata_sff_error_handler(ap); + ata_bmdma_error_handler(ap); } #ifdef CONFIG_PM @@ -1991,7 +1986,8 @@ static int nv_swncq_port_start(struct ata_port *ap) struct nv_swncq_port_priv *pp; int rc; - rc = ata_port_start(ap); + /* we might fallback to bmdma, allocate bmdma resources */ + rc = ata_bmdma_port_start(ap); if (rc) return rc; @@ -2016,7 +2012,7 @@ static int nv_swncq_port_start(struct ata_port *ap) static void nv_swncq_qc_prep(struct ata_queued_cmd *qc) { if (qc->tf.protocol != ATA_PROT_NCQ) { - ata_sff_qc_prep(qc); + ata_bmdma_qc_prep(qc); return; } @@ -2031,7 +2027,7 @@ static void nv_swncq_fill_sg(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; struct scatterlist *sg; struct nv_swncq_port_priv *pp = ap->private_data; - struct ata_prd *prd; + struct ata_bmdma_prd *prd; unsigned int si, idx; prd = pp->prd + ATA_MAX_PRD * qc->tag; @@ -2092,7 +2088,7 @@ static unsigned int nv_swncq_qc_issue(struct ata_queued_cmd *qc) struct nv_swncq_port_priv *pp = ap->private_data; if (qc->tf.protocol != ATA_PROT_NCQ) - return ata_sff_qc_issue(qc); + return ata_bmdma_qc_issue(qc); DPRINTK("Enter\n"); @@ -2380,16 +2376,14 @@ static irqreturn_t nv_swncq_interrupt(int irq, void *dev_instance) for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - if (ap->link.sactive) { - nv_swncq_host_interrupt(ap, (u16)irq_stat); - handled = 1; - } else { - if (irq_stat) /* reserve Hotplug */ - nv_swncq_irq_clear(ap, 0xfff0); + if (ap->link.sactive) { + nv_swncq_host_interrupt(ap, (u16)irq_stat); + handled = 1; + } else { + if (irq_stat) /* reserve Hotplug */ + nv_swncq_irq_clear(ap, 0xfff0); - handled += nv_host_intr(ap, (u8)irq_stat); - } + handled += nv_host_intr(ap, (u8)irq_stat); } irq_stat >>= NV_INT_PORT_SHIFT_MCP55; } @@ -2479,8 +2473,7 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } pci_set_master(pdev); - return ata_host_activate(host, pdev->irq, ipriv->irq_handler, - IRQF_SHARED, ipriv->sht); + return ata_pci_sff_activate_host(host, ipriv->irq_handler, ipriv->sht); } #ifdef CONFIG_PM diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 5356ec00d2b4..f03ad48273ff 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -333,7 +333,8 @@ static int pdc_common_port_start(struct ata_port *ap) struct pdc_port_priv *pp; int rc; - rc = ata_port_start(ap); + /* we use the same prd table as bmdma, allocate it */ + rc = ata_bmdma_port_start(ap); if (rc) return rc; @@ -499,7 +500,7 @@ static int pdc_sata_scr_write(struct ata_link *link, static void pdc_atapi_pkt(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - dma_addr_t sg_table = ap->prd_dma; + dma_addr_t sg_table = ap->bmdma_prd_dma; unsigned int cdb_len = qc->dev->cdb_len; u8 *cdb = qc->cdb; struct pdc_port_priv *pp = ap->private_data; @@ -587,6 +588,7 @@ static void pdc_atapi_pkt(struct ata_queued_cmd *qc) static void pdc_fill_sg(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; + struct ata_bmdma_prd *prd = ap->bmdma_prd; struct scatterlist *sg; const u32 SG_COUNT_ASIC_BUG = 41*4; unsigned int si, idx; @@ -613,8 +615,8 @@ static void pdc_fill_sg(struct ata_queued_cmd *qc) if ((offset + sg_len) > 0x10000) len = 0x10000 - offset; - ap->prd[idx].addr = cpu_to_le32(addr); - ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff); + prd[idx].addr = cpu_to_le32(addr); + prd[idx].flags_len = cpu_to_le32(len & 0xffff); VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); idx++; @@ -623,27 +625,27 @@ static void pdc_fill_sg(struct ata_queued_cmd *qc) } } - len = le32_to_cpu(ap->prd[idx - 1].flags_len); + len = le32_to_cpu(prd[idx - 1].flags_len); if (len > SG_COUNT_ASIC_BUG) { u32 addr; VPRINTK("Splitting last PRD.\n"); - addr = le32_to_cpu(ap->prd[idx - 1].addr); - ap->prd[idx - 1].flags_len = cpu_to_le32(len - SG_COUNT_ASIC_BUG); + addr = le32_to_cpu(prd[idx - 1].addr); + prd[idx - 1].flags_len = cpu_to_le32(len - SG_COUNT_ASIC_BUG); VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx - 1, addr, SG_COUNT_ASIC_BUG); addr = addr + len - SG_COUNT_ASIC_BUG; len = SG_COUNT_ASIC_BUG; - ap->prd[idx].addr = cpu_to_le32(addr); - ap->prd[idx].flags_len = cpu_to_le32(len); + prd[idx].addr = cpu_to_le32(addr); + prd[idx].flags_len = cpu_to_le32(len); VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); idx++; } - ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); + prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); } static void pdc_qc_prep(struct ata_queued_cmd *qc) @@ -658,7 +660,7 @@ static void pdc_qc_prep(struct ata_queued_cmd *qc) pdc_fill_sg(qc); /*FALLTHROUGH*/ case ATA_PROT_NODATA: - i = pdc_pkt_header(&qc->tf, qc->ap->prd_dma, + i = pdc_pkt_header(&qc->tf, qc->ap->bmdma_prd_dma, qc->dev->devno, pp->pkt); if (qc->tf.flags & ATA_TFLAG_LBA48) i = pdc_prep_lba48(&qc->tf, pp->pkt, i); @@ -838,7 +840,7 @@ static void pdc_error_handler(struct ata_port *ap) if (!(ap->pflags & ATA_PFLAG_FROZEN)) pdc_reset_port(ap); - ata_std_error_handler(ap); + ata_sff_error_handler(ap); } static void pdc_post_internal_cmd(struct ata_queued_cmd *qc) @@ -984,8 +986,7 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance) /* check for a plug or unplug event */ ata_no = pdc_port_no_to_ata_no(i, is_sataii_tx4); tmp = hotplug_status & (0x11 << ata_no); - if (tmp && ap && - !(ap->flags & ATA_FLAG_DISABLED)) { + if (tmp) { struct ata_eh_info *ehi = &ap->link.eh_info; ata_ehi_clear_desc(ehi); ata_ehi_hotplugged(ehi); @@ -997,8 +998,7 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance) /* check for a packet interrupt */ tmp = mask & (1 << (i + 1)); - if (tmp && ap && - !(ap->flags & ATA_FLAG_DISABLED)) { + if (tmp) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->link.active_tag); diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 92ba45e6689b..d533b3d20ca1 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -147,7 +147,6 @@ static struct ata_port_operations qs_ata_ops = { .prereset = qs_prereset, .softreset = ATA_OP_NULL, .error_handler = qs_error_handler, - .post_internal_cmd = ATA_OP_NULL, .lost_interrupt = ATA_OP_NULL, .scr_read = qs_scr_read, @@ -255,7 +254,7 @@ static int qs_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) static void qs_error_handler(struct ata_port *ap) { qs_enter_reg_mode(ap); - ata_std_error_handler(ap); + ata_sff_error_handler(ap); } static int qs_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) @@ -304,10 +303,8 @@ static void qs_qc_prep(struct ata_queued_cmd *qc) VPRINTK("ENTER\n"); qs_enter_reg_mode(qc->ap); - if (qc->tf.protocol != ATA_PROT_DMA) { - ata_sff_qc_prep(qc); + if (qc->tf.protocol != ATA_PROT_DMA) return; - } nelem = qs_fill_sg(qc); @@ -404,26 +401,24 @@ static inline unsigned int qs_intr_pkt(struct ata_host *host) u8 sHST = sff1 & 0x3f; /* host status */ unsigned int port_no = (sff1 >> 8) & 0x03; struct ata_port *ap = host->ports[port_no]; + struct qs_port_priv *pp = ap->private_data; + struct ata_queued_cmd *qc; DPRINTK("SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x\n", sff1, sff0, port_no, sHST, sDST); handled = 1; - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - struct qs_port_priv *pp = ap->private_data; - if (!pp || pp->state != qs_state_pkt) - continue; - qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { - switch (sHST) { - case 0: /* successful CPB */ - case 3: /* device error */ - qs_enter_reg_mode(qc->ap); - qs_do_or_die(qc, sDST); - break; - default: - break; - } + if (!pp || pp->state != qs_state_pkt) + continue; + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { + switch (sHST) { + case 0: /* successful CPB */ + case 3: /* device error */ + qs_enter_reg_mode(qc->ap); + qs_do_or_die(qc, sDST); + break; + default: + break; } } } @@ -436,33 +431,30 @@ static inline unsigned int qs_intr_mmio(struct ata_host *host) unsigned int handled = 0, port_no; for (port_no = 0; port_no < host->n_ports; ++port_no) { - struct ata_port *ap; - ap = host->ports[port_no]; - if (ap && - !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - struct qs_port_priv *pp; - qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (!qc || !(qc->flags & ATA_QCFLAG_ACTIVE)) { - /* - * The qstor hardware generates spurious - * interrupts from time to time when switching - * in and out of packet mode. - * There's no obvious way to know if we're - * here now due to that, so just ack the irq - * and pretend we knew it was ours.. (ugh). - * This does not affect packet mode. - */ - ata_sff_check_status(ap); - handled = 1; - continue; - } - pp = ap->private_data; - if (!pp || pp->state != qs_state_mmio) - continue; - if (!(qc->tf.flags & ATA_TFLAG_POLLING)) - handled |= ata_sff_host_intr(ap, qc); + struct ata_port *ap = host->ports[port_no]; + struct qs_port_priv *pp = ap->private_data; + struct ata_queued_cmd *qc; + + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (!qc) { + /* + * The qstor hardware generates spurious + * interrupts from time to time when switching + * in and out of packet mode. There's no + * obvious way to know if we're here now due + * to that, so just ack the irq and pretend we + * knew it was ours.. (ugh). This does not + * affect packet mode. + */ + ata_sff_check_status(ap); + handled = 1; + continue; } + + if (!pp || pp->state != qs_state_mmio) + continue; + if (!(qc->tf.flags & ATA_TFLAG_POLLING)) + handled |= ata_sff_host_intr(ap, qc); } return handled; } @@ -509,11 +501,7 @@ static int qs_port_start(struct ata_port *ap) void __iomem *mmio_base = qs_mmio_base(ap->host); void __iomem *chan = mmio_base + (ap->port_no * 0x4000); u64 addr; - int rc; - rc = ata_port_start(ap); - if (rc) - return rc; pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); if (!pp) return -ENOMEM; diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 3cb69d5fb817..2dda312b6b9a 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -284,7 +284,7 @@ static void sil_bmdma_setup(struct ata_queued_cmd *qc) void __iomem *bmdma = ap->ioaddr.bmdma_addr; /* load PRD table addr. */ - iowrite32(ap->prd_dma, bmdma + ATA_DMA_TABLE_OFS); + iowrite32(ap->bmdma_prd_dma, bmdma + ATA_DMA_TABLE_OFS); /* issue r/w command */ ap->ops->sff_exec_command(ap, &qc->tf); @@ -311,10 +311,10 @@ static void sil_fill_sg(struct ata_queued_cmd *qc) { struct scatterlist *sg; struct ata_port *ap = qc->ap; - struct ata_prd *prd, *last_prd = NULL; + struct ata_bmdma_prd *prd, *last_prd = NULL; unsigned int si; - prd = &ap->prd[0]; + prd = &ap->bmdma_prd[0]; for_each_sg(qc->sg, sg, qc->n_elem, si) { /* Note h/w doesn't support 64-bit, so we unconditionally * truncate dma_addr_t to u32. @@ -532,9 +532,6 @@ static irqreturn_t sil_interrupt(int irq, void *dev_instance) struct ata_port *ap = host->ports[i]; u32 bmdma2 = readl(mmio_base + sil_port[ap->port_no].bmdma2); - if (unlikely(ap->flags & ATA_FLAG_DISABLED)) - continue; - /* turn off SATA_IRQ if not supported */ if (ap->flags & SIL_FLAG_NO_SATA_IRQ) bmdma2 &= ~SIL_DMA_SATA_IRQ; diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 433b6b89c795..e9250514734b 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -1160,13 +1160,8 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance) for (i = 0; i < host->n_ports; i++) if (status & (1 << i)) { - struct ata_port *ap = host->ports[i]; - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - sil24_host_intr(ap); - handled++; - } else - printk(KERN_ERR DRV_NAME - ": interrupt from disabled port %d\n", i); + sil24_host_intr(host->ports[i]); + handled++; } spin_unlock(&host->lock); diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index 7257f2d5c52c..101fd6a19829 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -224,7 +224,7 @@ static void k2_bmdma_setup_mmio(struct ata_queued_cmd *qc) /* load PRD table addr. */ mb(); /* make sure PRD table writes are visible to controller */ - writel(ap->prd_dma, mmio + ATA_DMA_TABLE_OFS); + writel(ap->bmdma_prd_dma, mmio + ATA_DMA_TABLE_OFS); /* specify data direction, triple-check start bit is clear */ dmactl = readb(mmio + ATA_DMA_CMD); diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index 232468f2ea90..bedd5188e5b0 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -302,11 +302,6 @@ static int pdc_port_start(struct ata_port *ap) { struct device *dev = ap->host->dev; struct pdc_port_priv *pp; - int rc; - - rc = ata_port_start(ap); - if (rc) - return rc; pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); if (!pp) @@ -840,8 +835,7 @@ static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance) ap = host->ports[port_no]; tmp = mask & (1 << i); VPRINTK("seq %u, port_no %u, ap %p, tmp %x\n", i, port_no, ap, tmp); - if (tmp && ap && - !(ap->flags & ATA_FLAG_DISABLED)) { + if (tmp && ap) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->link.active_tag); @@ -927,7 +921,7 @@ static void pdc_error_handler(struct ata_port *ap) if (!(ap->pflags & ATA_PFLAG_FROZEN)) pdc_reset_port(ap); - ata_std_error_handler(ap); + ata_sff_error_handler(ap); } static void pdc_post_internal_cmd(struct ata_queued_cmd *qc) diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index 011e098590d1..d8dac17dc2c8 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -181,9 +181,7 @@ static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - rc = ata_pci_bmdma_init(host); - if (rc) - return rc; + ata_pci_bmdma_init(host); iomap = host->iomap; diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index 8b2a278b2547..2107952ebff1 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -284,14 +284,8 @@ static irqreturn_t vsc_sata_interrupt(int irq, void *dev_instance) for (i = 0; i < host->n_ports; i++) { u8 port_status = (status >> (8 * i)) & 0xff; if (port_status) { - struct ata_port *ap = host->ports[i]; - - if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - vsc_port_intr(port_status, ap); - handled++; - } else - dev_printk(KERN_ERR, host->dev, - "interrupt from disabled port %d\n", i); + vsc_port_intr(port_status, host->ports[i]); + handled++; } } diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c index 8ad4ffea6920..6e6b6a11b3ce 100644 --- a/drivers/base/iommu.c +++ b/drivers/base/iommu.c @@ -80,20 +80,6 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) } EXPORT_SYMBOL_GPL(iommu_detach_device); -int iommu_map_range(struct iommu_domain *domain, unsigned long iova, - phys_addr_t paddr, size_t size, int prot) -{ - return iommu_ops->map(domain, iova, paddr, size, prot); -} -EXPORT_SYMBOL_GPL(iommu_map_range); - -void iommu_unmap_range(struct iommu_domain *domain, unsigned long iova, - size_t size) -{ - iommu_ops->unmap(domain, iova, size); -} -EXPORT_SYMBOL_GPL(iommu_unmap_range); - phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { @@ -107,3 +93,32 @@ int iommu_domain_has_cap(struct iommu_domain *domain, return iommu_ops->domain_has_cap(domain, cap); } EXPORT_SYMBOL_GPL(iommu_domain_has_cap); + +int iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, int gfp_order, int prot) +{ + unsigned long invalid_mask; + size_t size; + + size = 0x1000UL << gfp_order; + invalid_mask = size - 1; + + BUG_ON((iova | paddr) & invalid_mask); + + return iommu_ops->map(domain, iova, paddr, gfp_order, prot); +} +EXPORT_SYMBOL_GPL(iommu_map); + +int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order) +{ + unsigned long invalid_mask; + size_t size; + + size = 0x1000UL << gfp_order; + invalid_mask = size - 1; + + BUG_ON(iova & invalid_mask); + + return iommu_ops->unmap(domain, iova, gfp_order); +} +EXPORT_SYMBOL_GPL(iommu_unmap); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 4b4b565c835f..ada6397c23a5 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -187,7 +187,7 @@ EXPORT_SYMBOL_GPL(platform_device_alloc); * released. */ int platform_device_add_resources(struct platform_device *pdev, - struct resource *res, unsigned int num) + const struct resource *res, unsigned int num) { struct resource *r; @@ -367,7 +367,7 @@ EXPORT_SYMBOL_GPL(platform_device_unregister); */ struct platform_device *platform_device_register_simple(const char *name, int id, - struct resource *res, + const struct resource *res, unsigned int num) { struct platform_device *pdev; @@ -967,17 +967,17 @@ static int platform_pm_restore_noirq(struct device *dev) int __weak platform_pm_runtime_suspend(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_suspend(dev); }; int __weak platform_pm_runtime_resume(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_resume(dev); }; int __weak platform_pm_runtime_idle(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_idle(dev); }; #else /* !CONFIG_PM_RUNTIME */ @@ -1254,6 +1254,26 @@ static int __init early_platform_driver_probe_id(char *class_str, } if (match) { + /* + * Set up a sensible init_name to enable + * dev_name() and others to be used before the + * rest of the driver core is initialized. + */ + if (!match->dev.init_name && slab_is_available()) { + if (match->id != -1) + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s.%d", + match->name, + match->id); + else + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s", + match->name); + + if (!match->dev.init_name) + return -ENOMEM; + } + if (epdrv->pdrv->probe(match)) pr_warning("%s: unable to probe %s early.\n", class_str, match->name); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 626dd147b75f..b0ec0e9f27e9 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -229,14 +229,16 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) if (retval) { dev->power.runtime_status = RPM_ACTIVE; - pm_runtime_cancel_pending(dev); - if (retval == -EAGAIN || retval == -EBUSY) { - notify = true; + if (dev->power.timer_expires == 0) + notify = true; dev->power.runtime_error = 0; + } else { + pm_runtime_cancel_pending(dev); } } else { dev->power.runtime_status = RPM_SUSPENDED; + pm_runtime_deactivate_timer(dev); if (dev->parent) { parent = dev->parent; @@ -659,8 +661,6 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay) if (dev->power.runtime_status == RPM_SUSPENDED) retval = 1; - else if (dev->power.runtime_status == RPM_SUSPENDING) - retval = -EINPROGRESS; else if (atomic_read(&dev->power.usage_count) > 0 || dev->power.disable_depth > 0) retval = -EAGAIN; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 86fd9373447e..a4c33bc51257 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -5,6 +5,7 @@ #include <linux/device.h> #include <linux/string.h> #include <linux/pm_runtime.h> +#include <asm/atomic.h> #include "power.h" /* @@ -143,7 +144,59 @@ wake_store(struct device * dev, struct device_attribute *attr, static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); -#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG +#ifdef CONFIG_PM_ADVANCED_DEBUG +#ifdef CONFIG_PM_RUNTIME + +static ssize_t rtpm_usagecount_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", atomic_read(&dev->power.usage_count)); +} + +static ssize_t rtpm_children_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev->power.ignore_children ? + 0 : atomic_read(&dev->power.child_count)); +} + +static ssize_t rtpm_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if ((dev->power.disable_depth) && (dev->power.runtime_auto == false)) + return sprintf(buf, "disabled & forbidden\n"); + else if (dev->power.disable_depth) + return sprintf(buf, "disabled\n"); + else if (dev->power.runtime_auto == false) + return sprintf(buf, "forbidden\n"); + return sprintf(buf, "enabled\n"); +} + +static ssize_t rtpm_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (dev->power.runtime_error) + return sprintf(buf, "error\n"); + switch (dev->power.runtime_status) { + case RPM_SUSPENDED: + return sprintf(buf, "suspended\n"); + case RPM_SUSPENDING: + return sprintf(buf, "suspending\n"); + case RPM_RESUMING: + return sprintf(buf, "resuming\n"); + case RPM_ACTIVE: + return sprintf(buf, "active\n"); + } + return -EIO; +} + +static DEVICE_ATTR(runtime_usage, 0444, rtpm_usagecount_show, NULL); +static DEVICE_ATTR(runtime_active_kids, 0444, rtpm_children_show, NULL); +static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL); +static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL); + +#endif + static ssize_t async_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -170,15 +223,21 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(async, 0644, async_show, async_store); -#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */ +#endif /* CONFIG_PM_ADVANCED_DEBUG */ static struct attribute * power_attrs[] = { #ifdef CONFIG_PM_RUNTIME &dev_attr_control.attr, #endif &dev_attr_wakeup.attr, -#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG +#ifdef CONFIG_PM_ADVANCED_DEBUG &dev_attr_async.attr, +#ifdef CONFIG_PM_RUNTIME + &dev_attr_runtime_usage.attr, + &dev_attr_runtime_active_kids.attr, + &dev_attr_runtime_status.attr, + &dev_attr_runtime_enabled.attr, +#endif #endif NULL, }; diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 0182a22c423a..832798aa14f6 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -66,6 +66,7 @@ #include <linux/blkdev.h> #include <linux/elevator.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> #include <asm/setup.h> #include <asm/uaccess.h> @@ -1696,34 +1697,18 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data) return get_disk(unit[drive].gendisk); } -static int __init amiga_floppy_init(void) +static int __init amiga_floppy_probe(struct platform_device *pdev) { int i, ret; - if (!MACH_IS_AMIGA) - return -ENODEV; - - if (!AMIGAHW_PRESENT(AMI_FLOPPY)) - return -ENODEV; - if (register_blkdev(FLOPPY_MAJOR,"fd")) return -EBUSY; - /* - * We request DSKPTR, DSKLEN and DSKDATA only, because the other - * floppy registers are too spreaded over the custom register space - */ - ret = -EBUSY; - if (!request_mem_region(CUSTOM_PHYSADDR+0x20, 8, "amiflop [Paula]")) { - printk("fd: cannot get floppy registers\n"); - goto out_blkdev; - } - ret = -ENOMEM; if ((raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE, "Floppy")) == NULL) { printk("fd: cannot get chip mem buffer\n"); - goto out_memregion; + goto out_blkdev; } ret = -EBUSY; @@ -1792,18 +1777,13 @@ out_irq2: free_irq(IRQ_AMIGA_DSKBLK, NULL); out_irq: amiga_chip_free(raw_buf); -out_memregion: - release_mem_region(CUSTOM_PHYSADDR+0x20, 8); out_blkdev: unregister_blkdev(FLOPPY_MAJOR,"fd"); return ret; } -module_init(amiga_floppy_init); -#ifdef MODULE - #if 0 /* not safe to unload */ -void cleanup_module(void) +static int __exit amiga_floppy_remove(struct platform_device *pdev) { int i; @@ -1820,12 +1800,25 @@ void cleanup_module(void) custom.dmacon = DMAF_DISK; /* disable DMA */ amiga_chip_free(raw_buf); blk_cleanup_queue(floppy_queue); - release_mem_region(CUSTOM_PHYSADDR+0x20, 8); unregister_blkdev(FLOPPY_MAJOR, "fd"); } #endif -#else +static struct platform_driver amiga_floppy_driver = { + .driver = { + .name = "amiga-floppy", + .owner = THIS_MODULE, + }, +}; + +static int __init amiga_floppy_init(void) +{ + return platform_driver_probe(&amiga_floppy_driver, amiga_floppy_probe); +} + +module_init(amiga_floppy_init); + +#ifndef MODULE static int __init amiga_floppy_setup (char *str) { int n; @@ -1840,3 +1833,5 @@ static int __init amiga_floppy_setup (char *str) __setup("floppy=", amiga_floppy_setup); #endif + +MODULE_ALIAS("platform:amiga-floppy"); diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index eb5ff0531cfb..51ceaee98f9f 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1588,7 +1588,6 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, c->Request = ioc->Request; if (ioc->buf_size > 0) { - int i; for (i = 0; i < sg_used; i++) { temp64.val = pci_map_single(host->pdev, buff[i], @@ -2434,7 +2433,7 @@ static int deregister_disk(ctlr_info_t *h, int drv_index, /* if it was the last disk, find the new hightest lun */ if (clear_all && recalculate_highest_lun) { - int i, newhighest = -1; + int newhighest = -1; for (i = 0; i <= h->highest_lun; i++) { /* if the disk has size > 0, it is available */ if (h->drv[i] && h->drv[i]->heads) diff --git a/drivers/block/hd.c b/drivers/block/hd.c index 034e6dfc878c..81c78b3ce2df 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -164,12 +164,12 @@ unsigned long read_timer(void) unsigned long t, flags; int i; - spin_lock_irqsave(&i8253_lock, flags); + raw_spin_lock_irqsave(&i8253_lock, flags); t = jiffies * 11932; outb_p(0, 0x43); i = inb_p(0x40); i |= inb(0x40) << 8; - spin_unlock_irqrestore(&i8253_lock, flags); + raw_spin_unlock_irqrestore(&i8253_lock, flags); return(t - i); } #endif diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index d9bf87ca9e83..6f907ebed2d5 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -65,7 +65,6 @@ MODULE_LICENSE("GPL"); typedef struct bluecard_info_t { struct pcmcia_device *p_dev; - dev_node_t node; struct hci_dev *hdev; @@ -869,9 +868,6 @@ static int bluecard_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - - link->irq.Handler = bluecard_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -908,9 +904,9 @@ static int bluecard_config(struct pcmcia_device *link) if (i != 0) goto failed; - i = pcmcia_request_irq(link, &link->irq); + i = pcmcia_request_irq(link, bluecard_interrupt); if (i != 0) - link->irq.AssignedIRQ = 0; + goto failed; i = pcmcia_request_configuration(link, &link->conf); if (i != 0) @@ -919,9 +915,6 @@ static int bluecard_config(struct pcmcia_device *link) if (bluecard_open(info) != 0) goto failed; - strcpy(info->node.dev_name, info->hdev->name); - link->dev_node = &info->node; - return 0; failed: diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 027cb8bf650f..21e05fdc9121 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -72,7 +72,6 @@ MODULE_FIRMWARE("BT3CPCC.bin"); typedef struct bt3c_info_t { struct pcmcia_device *p_dev; - dev_node_t node; struct hci_dev *hdev; @@ -661,9 +660,6 @@ static int bt3c_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - - link->irq.Handler = bt3c_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -743,9 +739,9 @@ static int bt3c_config(struct pcmcia_device *link) goto failed; found_port: - i = pcmcia_request_irq(link, &link->irq); + i = pcmcia_request_irq(link, &bt3c_interrupt); if (i != 0) - link->irq.AssignedIRQ = 0; + goto failed; i = pcmcia_request_configuration(link, &link->conf); if (i != 0) @@ -754,9 +750,6 @@ found_port: if (bt3c_open(info) != 0) goto failed; - strcpy(info->node.dev_name, info->hdev->name); - link->dev_node = &info->node; - return 0; failed: diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 60c0953d7d00..4ed7288f99db 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -67,7 +67,6 @@ MODULE_LICENSE("GPL"); typedef struct btuart_info_t { struct pcmcia_device *p_dev; - dev_node_t node; struct hci_dev *hdev; @@ -590,9 +589,6 @@ static int btuart_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - - link->irq.Handler = btuart_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -672,9 +668,9 @@ static int btuart_config(struct pcmcia_device *link) goto failed; found_port: - i = pcmcia_request_irq(link, &link->irq); + i = pcmcia_request_irq(link, btuart_interrupt); if (i != 0) - link->irq.AssignedIRQ = 0; + goto failed; i = pcmcia_request_configuration(link, &link->conf); if (i != 0) @@ -683,9 +679,6 @@ found_port: if (btuart_open(info) != 0) goto failed; - strcpy(info->node.dev_name, info->hdev->name); - link->dev_node = &info->node; - return 0; failed: diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 17788317c51a..ef044d55cb25 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -67,7 +67,6 @@ MODULE_LICENSE("GPL"); typedef struct dtl1_info_t { struct pcmcia_device *p_dev; - dev_node_t node; struct hci_dev *hdev; @@ -575,9 +574,6 @@ static int dtl1_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - - link->irq.Handler = dtl1_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -621,9 +617,9 @@ static int dtl1_config(struct pcmcia_device *link) if (pcmcia_loop_config(link, dtl1_confcheck, NULL) < 0) goto failed; - i = pcmcia_request_irq(link, &link->irq); + i = pcmcia_request_irq(link, dtl1_interrupt); if (i != 0) - link->irq.AssignedIRQ = 0; + goto failed; i = pcmcia_request_configuration(link, &link->conf); if (i != 0) @@ -632,9 +628,6 @@ static int dtl1_config(struct pcmcia_device *link) if (dtl1_open(info) != 0) goto failed; - strcpy(info->node.dev_name, info->hdev->name); - link->dev_node = &info->node; - return 0; failed: diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index fb86708e47ed..4b51982fd23a 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1214,7 +1214,7 @@ struct agp_memory *agp_generic_alloc_user(size_t page_count, int type) return NULL; for (i = 0; i < page_count; i++) - new->pages[i] = 0; + new->pages[i] = NULL; new->page_count = 0; new->type = type; new->num_scratch_pages = pages; diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 7fef305774de..89d871ef8c2f 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -253,7 +253,7 @@ static int bsr_add_node(struct device_node *bn) cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev, cur, cur->bsr_name); - if (!cur->bsr_device) { + if (IS_ERR(cur->bsr_device)) { printk(KERN_ERR "device_create failed for %s\n", cur->bsr_name); cdev_del(&cur->bsr_cdev); diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index fc8cf7ac7f2b..4cd8b227c11f 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -23,6 +23,7 @@ #include <linux/seq_file.h> #include <linux/dmi.h> #include <linux/capability.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -82,8 +83,7 @@ module_param(fan_mult, int, 0); MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); static int i8k_open_fs(struct inode *inode, struct file *file); -static int i8k_ioctl(struct inode *, struct file *, unsigned int, - unsigned long); +static long i8k_ioctl(struct file *, unsigned int, unsigned long); static const struct file_operations i8k_fops = { .owner = THIS_MODULE, @@ -91,7 +91,7 @@ static const struct file_operations i8k_fops = { .read = seq_read, .llseek = seq_lseek, .release = single_release, - .ioctl = i8k_ioctl, + .unlocked_ioctl = i8k_ioctl, }; struct smm_regs { @@ -307,8 +307,8 @@ static int i8k_get_dell_signature(int req_fn) return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; } -static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, - unsigned long arg) +static int +i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) { int val = 0; int speed; @@ -395,6 +395,17 @@ static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, return 0; } +static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + long ret; + + lock_kernel(); + ret = i8k_ioctl_unlocked(fp, cmd, arg); + unlock_kernel(); + + return ret; +} + /* * Print the information for /proc/i8k. */ diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 90b199f97bec..e7956acf2ad6 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -106,7 +106,6 @@ static int major; /* major number we get from the kernel */ struct cm4000_dev { struct pcmcia_device *p_dev; - dev_node_t node; /* OS node (major,minor) */ unsigned char atr[MAX_ATR]; unsigned char rbuf[512]; @@ -884,8 +883,7 @@ static void monitor_card(unsigned long p) /* slow down warning, but prompt immediately after insertion */ if (dev->cwarn == 0 || dev->cwarn == 10) { set_bit(IS_BAD_CARD, &dev->flags); - printk(KERN_WARNING MODULE_NAME ": device %s: ", - dev->node.dev_name); + dev_warn(&dev->p_dev->dev, MODULE_NAME ": "); if (test_bit(IS_BAD_CSUM, &dev->flags)) { DEBUGP(4, dev, "ATR checksum (0x%.2x, should " "be zero) failed\n", dev->atr_csum); @@ -1781,11 +1779,6 @@ static int cm4000_config(struct pcmcia_device * link, int devno) goto cs_release; dev = link->priv; - sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno); - dev->node.major = major; - dev->node.minor = devno; - dev->node.next = NULL; - link->dev_node = &dev->node; return 0; diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index a6a70e476bea..c0775c844e08 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -72,7 +72,6 @@ static struct class *cmx_class; struct reader_dev { struct pcmcia_device *p_dev; - dev_node_t node; wait_queue_head_t devq; wait_queue_head_t poll_wait; wait_queue_head_t read_wait; @@ -568,10 +567,6 @@ static int reader_config(struct pcmcia_device *link, int devno) } dev = link->priv; - sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno); - dev->node.major = major; - dev->node.minor = devno; - dev->node.next = &dev->node; DEBUGP(2, dev, "device " DEVICE_NAME "%d at 0x%.4x-0x%.4x\n", devno, link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1); diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c index dff24dae1485..63c32e3f23ba 100644 --- a/drivers/char/pcmcia/ipwireless/main.c +++ b/drivers/char/pcmcia/ipwireless/main.c @@ -195,9 +195,6 @@ static int config_ipwireless(struct ipw_dev *ipw) link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = ipwireless_interrupt; - INIT_WORK(&ipw->work_reboot, signalled_reboot_work); ipwireless_init_hardware_v1(ipw->hardware, link->io.BasePort1, @@ -205,8 +202,7 @@ static int config_ipwireless(struct ipw_dev *ipw) ipw->is_v2_card, signalled_reboot_callback, ipw); - ret = pcmcia_request_irq(link, &link->irq); - + ret = pcmcia_request_irq(link, ipwireless_interrupt); if (ret != 0) goto exit; @@ -217,7 +213,7 @@ static int config_ipwireless(struct ipw_dev *ipw) (unsigned int) link->io.BasePort1, (unsigned int) (link->io.BasePort1 + link->io.NumPorts1 - 1), - (unsigned int) link->irq.AssignedIRQ); + (unsigned int) link->irq); if (ipw->attr_memory && ipw->common_memory) printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": attr memory 0x%08lx-0x%08lx, common memory 0x%08lx-0x%08lx\n", @@ -232,8 +228,7 @@ static int config_ipwireless(struct ipw_dev *ipw) if (!ipw->network) goto exit; - ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network, - ipw->nodes); + ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network); if (!ipw->tty) goto exit; @@ -248,8 +243,6 @@ static int config_ipwireless(struct ipw_dev *ipw) if (ret != 0) goto exit; - link->dev_node = &ipw->nodes[0]; - return 0; exit: @@ -271,8 +264,6 @@ exit: static void release_ipwireless(struct ipw_dev *ipw) { - pcmcia_disable_device(ipw->link); - if (ipw->common_memory) { release_mem_region(ipw->request_common_memory.Base, ipw->request_common_memory.Size); @@ -288,7 +279,6 @@ static void release_ipwireless(struct ipw_dev *ipw) if (ipw->attr_memory) pcmcia_release_window(ipw->link, ipw->handle_attr_memory); - /* Break the link with Card Services */ pcmcia_disable_device(ipw->link); } @@ -313,9 +303,6 @@ static int ipwireless_attach(struct pcmcia_device *link) ipw->link = link; link->priv = ipw; - /* Link this device into our device list. */ - link->dev_node = &ipw->nodes[0]; - ipw->hardware = ipwireless_hardware_create(); if (!ipw->hardware) { kfree(ipw); diff --git a/drivers/char/pcmcia/ipwireless/main.h b/drivers/char/pcmcia/ipwireless/main.h index 0e0363af9ab2..96d0ef31b172 100644 --- a/drivers/char/pcmcia/ipwireless/main.h +++ b/drivers/char/pcmcia/ipwireless/main.h @@ -54,7 +54,6 @@ struct ipw_dev { void __iomem *common_memory; win_req_t request_common_memory; - dev_node_t nodes[2]; /* Reference to attribute memory, containing CIS data */ void *attribute_memory; diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 2bb7874a6899..1a2c2c3b068f 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -487,7 +487,7 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, return tty_mode_ioctl(linux_tty, file, cmd , arg); } -static int add_tty(dev_node_t *nodesp, int j, +static int add_tty(int j, struct ipw_hardware *hardware, struct ipw_network *network, int channel_idx, int secondary_channel_idx, int tty_type) @@ -510,19 +510,13 @@ static int add_tty(dev_node_t *nodesp, int j, ipwireless_associate_network_tty(network, secondary_channel_idx, ttys[j]); - if (nodesp != NULL) { - sprintf(nodesp->dev_name, "ttyIPWp%d", j); - nodesp->major = ipw_tty_driver->major; - nodesp->minor = j + ipw_tty_driver->minor_start; - } if (get_tty(j + ipw_tty_driver->minor_start) == ttys[j]) report_registering(ttys[j]); return 0; } struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware, - struct ipw_network *network, - dev_node_t *nodes) + struct ipw_network *network) { int i, j; @@ -539,26 +533,23 @@ struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware, if (allfree) { j = i; - if (add_tty(&nodes[0], j, hardware, network, + if (add_tty(j, hardware, network, IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS, TTYTYPE_MODEM)) return NULL; j += IPWIRELESS_PCMCIA_MINOR_RANGE; - if (add_tty(&nodes[1], j, hardware, network, + if (add_tty(j, hardware, network, IPW_CHANNEL_DIALLER, -1, TTYTYPE_MONITOR)) return NULL; j += IPWIRELESS_PCMCIA_MINOR_RANGE; - if (add_tty(NULL, j, hardware, network, + if (add_tty(j, hardware, network, IPW_CHANNEL_RAS, -1, TTYTYPE_RAS_RAW)) return NULL; - nodes[0].next = &nodes[1]; - nodes[1].next = NULL; - return ttys[i]; } } diff --git a/drivers/char/pcmcia/ipwireless/tty.h b/drivers/char/pcmcia/ipwireless/tty.h index b0deb9168b6b..4da6c201f727 100644 --- a/drivers/char/pcmcia/ipwireless/tty.h +++ b/drivers/char/pcmcia/ipwireless/tty.h @@ -34,8 +34,7 @@ int ipwireless_tty_init(void); void ipwireless_tty_release(void); struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hw, - struct ipw_network *net, - dev_node_t *nodes); + struct ipw_network *net); void ipwireless_tty_free(struct ipw_tty *tty); void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, unsigned int length); diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index c31a0d913d37..308903ec8bf8 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -220,7 +220,6 @@ typedef struct _mgslpc_info { /* PCMCIA support */ struct pcmcia_device *p_dev; - dev_node_t node; int stop; /* SPPP/Cisco HDLC device parts */ @@ -552,10 +551,6 @@ static int mgslpc_probe(struct pcmcia_device *link) /* Initialize the struct pcmcia_device structure */ - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = NULL; - link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; @@ -608,9 +603,7 @@ static int mgslpc_config(struct pcmcia_device *link) link->conf.ConfigIndex = 8; link->conf.Present = PRESENT_OPTION; - link->irq.Handler = mgslpc_isr; - - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, mgslpc_isr); if (ret) goto failed; ret = pcmcia_request_configuration(link, &link->conf); @@ -618,17 +611,12 @@ static int mgslpc_config(struct pcmcia_device *link) goto failed; info->io_base = link->io.BasePort1; - info->irq_level = link->irq.AssignedIRQ; - - /* add to linked list of devices */ - sprintf(info->node.dev_name, "mgslpc0"); - info->node.major = info->node.minor = 0; - link->dev_node = &info->node; + info->irq_level = link->irq; - printk(KERN_INFO "%s: index 0x%02x:", - info->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x:", + link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 8dfd24721a82..78a62ebe75c7 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -627,7 +627,6 @@ static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) char data; int char_count; int save_cnt; - int len; /* determine the channel and change to that context */ channel = (u_short) (base_addr[CyLICR] >> 2); @@ -1528,7 +1527,6 @@ static int cy_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - unsigned long val; struct cyclades_port *info = tty->driver_data; int ret_val = 0; void __user *argp = (void __user *)arg; diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 193f9c214946..5d15630a5830 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -293,7 +293,7 @@ static struct sysrq_key_op sysrq_showstate_blocked_op = { static void sysrq_ftrace_dump(int key, struct tty_struct *tty) { - ftrace_dump(); + ftrace_dump(DUMP_ALL); } static struct sysrq_key_op sysrq_ftrace_dump_op = { .handler = sysrq_ftrace_dump, diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index f5fc64f89c5c..4dc338f3d1aa 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -17,14 +17,16 @@ menuconfig TCG_TPM obtained at: <http://sourceforge.net/projects/trousers>. To compile this driver as a module, choose M here; the module will be called tpm. If unsure, say N. - Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI + Notes: + 1) For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI and CONFIG_PNPACPI. + 2) Without ACPI enabled, the BIOS event log won't be accessible, + which is required to validate the PCR 0-7 values. if TCG_TPM config TCG_TIS tristate "TPM Interface Specification 1.2 Interface" - depends on PNP ---help--- If you have a TPM security chip that is compliant with the TCG TIS 1.2 TPM specification say Yes and it will be accessible diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 068c816e6942..05ad4a17a28f 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1068,6 +1068,27 @@ void tpm_remove_hardware(struct device *dev) } EXPORT_SYMBOL_GPL(tpm_remove_hardware); +#define TPM_ORD_SAVESTATE cpu_to_be32(152) +#define SAVESTATE_RESULT_SIZE 10 + +static struct tpm_input_header savestate_header = { + .tag = TPM_TAG_RQU_COMMAND, + .length = cpu_to_be32(10), + .ordinal = TPM_ORD_SAVESTATE +}; + +/* Bug workaround - some TPM's don't flush the most + * recently changed pcr on suspend, so force the flush + * with an extend to the selected _unused_ non-volatile pcr. + */ +static int tpm_suspend_pcr; +static int __init tpm_suspend_setup(char *str) +{ + get_option(&str, &tpm_suspend_pcr); + return 1; +} +__setup("tpm_suspend_pcr=", tpm_suspend_setup); + /* * We are about to suspend. Save the TPM state * so that it can be restored. @@ -1075,17 +1096,29 @@ EXPORT_SYMBOL_GPL(tpm_remove_hardware); int tpm_pm_suspend(struct device *dev, pm_message_t pm_state) { struct tpm_chip *chip = dev_get_drvdata(dev); - u8 savestate[] = { - 0, 193, /* TPM_TAG_RQU_COMMAND */ - 0, 0, 0, 10, /* blob length (in bytes) */ - 0, 0, 0, 152 /* TPM_ORD_SaveState */ - }; + struct tpm_cmd_t cmd; + int rc; + + u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 }; if (chip == NULL) return -ENODEV; - tpm_transmit(chip, savestate, sizeof(savestate)); - return 0; + /* for buggy tpm, flush pcrs with extend to selected dummy */ + if (tpm_suspend_pcr) { + cmd.header.in = pcrextend_header; + cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr); + memcpy(cmd.params.pcrextend_in.hash, dummy_hash, + TPM_DIGEST_SIZE); + rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + "extending dummy pcr before suspend"); + } + + /* now do the actual savestate */ + cmd.header.in = savestate_header; + rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, + "sending savestate before suspend"); + return rc; } EXPORT_SYMBOL_GPL(tpm_pm_suspend); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 94345994f8a6..24314a9cffe8 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -598,7 +598,7 @@ out_err: tpm_remove_hardware(chip->dev); return rc; } - +#ifdef CONFIG_PNP static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, const struct pnp_device_id *pnp_id) { @@ -663,7 +663,7 @@ static struct pnp_driver tis_pnp_driver = { module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444); MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); - +#endif static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg) { return tpm_pm_suspend(&dev->dev, msg); @@ -690,21 +690,21 @@ MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry"); static int __init init_tis(void) { int rc; +#ifdef CONFIG_PNP + if (!force) + return pnp_register_driver(&tis_pnp_driver); +#endif - if (force) { - rc = platform_driver_register(&tis_drv); - if (rc < 0) - return rc; - if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) - return PTR_ERR(pdev); - if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) { - platform_device_unregister(pdev); - platform_driver_unregister(&tis_drv); - } + rc = platform_driver_register(&tis_drv); + if (rc < 0) return rc; + if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) + return PTR_ERR(pdev); + if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) { + platform_device_unregister(pdev); + platform_driver_unregister(&tis_drv); } - - return pnp_register_driver(&tis_pnp_driver); + return rc; } static void __exit cleanup_tis(void) @@ -728,12 +728,14 @@ static void __exit cleanup_tis(void) list_del(&i->list); } spin_unlock(&tis_lock); - - if (force) { - platform_device_unregister(pdev); - platform_driver_unregister(&tis_drv); - } else +#ifdef CONFIG_PNP + if (!force) { pnp_unregister_driver(&tis_pnp_driver); + return; + } +#endif + platform_device_unregister(pdev); + platform_driver_unregister(&tis_drv); } module_init(init_tis); diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c index b314a999aabe..d7be69f13154 100644 --- a/drivers/clocksource/cs5535-clockevt.c +++ b/drivers/clocksource/cs5535-clockevt.c @@ -154,14 +154,14 @@ static int __init cs5535_mfgpt_init(void) if (cs5535_mfgpt_setup_irq(timer, MFGPT_CMP2, &timer_irq)) { printk(KERN_ERR DRV_NAME ": Could not set up IRQ %d\n", timer_irq); - return -EIO; + goto err_timer; } /* And register it with the kernel */ ret = setup_irq(timer_irq, &mfgptirq); if (ret) { printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n"); - goto err; + goto err_irq; } /* Set the clock scale and enable the event mode for CMP2 */ @@ -184,8 +184,10 @@ static int __init cs5535_mfgpt_init(void) return 0; -err: +err_irq: cs5535_mfgpt_release_irq(cs5535_event_clock, MFGPT_CMP2, &timer_irq); +err_timer: + cs5535_mfgpt_free_timer(cs5535_event_clock); printk(KERN_ERR DRV_NAME ": Unable to set up the MFGPT clock source\n"); return -EIO; } diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 744f748cc84b..f6677cb19789 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -150,13 +150,12 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; int ret; /* enable clock */ ret = clk_enable(p->clk); if (ret) { - pr_err("sh_cmt: cannot enable clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot enable clock\n"); return ret; } @@ -279,7 +278,7 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p, delay = 1; if (!delay) - pr_warning("sh_cmt: too long delay\n"); + dev_warn(&p->pdev->dev, "too long delay\n"); } while (delay); } @@ -289,7 +288,7 @@ static void sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta) unsigned long flags; if (delta > p->max_match_value) - pr_warning("sh_cmt: delta out of range\n"); + dev_warn(&p->pdev->dev, "delta out of range\n"); spin_lock_irqsave(&p->lock, flags); p->next_match_value = delta; @@ -451,7 +450,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, cs->resume = sh_cmt_clocksource_resume; cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; - pr_info("sh_cmt: %s used as clock source\n", cs->name); + dev_info(&p->pdev->dev, "used as clock source\n"); clocksource_register(cs); return 0; } @@ -497,13 +496,11 @@ static void sh_cmt_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - pr_info("sh_cmt: %s used for periodic clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for periodic clock events\n"); sh_cmt_clock_event_start(p, 1); break; case CLOCK_EVT_MODE_ONESHOT: - pr_info("sh_cmt: %s used for oneshot clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for oneshot clock events\n"); sh_cmt_clock_event_start(p, 0); break; case CLOCK_EVT_MODE_SHUTDOWN: @@ -544,7 +541,7 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p, ced->set_next_event = sh_cmt_clock_event_next; ced->set_mode = sh_cmt_clock_event_mode; - pr_info("sh_cmt: %s used for clock events\n", ced->name); + dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); } @@ -601,22 +598,27 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) /* map memory, let mapbase point to our channel */ p->mapbase = ioremap_nocache(res->start, resource_size(res)); if (p->mapbase == NULL) { - pr_err("sh_cmt: failed to remap I/O memory\n"); + dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* request irq using setup_irq() (too early for request_irq()) */ - p->irqaction.name = cfg->name; + p->irqaction.name = dev_name(&p->pdev->dev); p->irqaction.handler = sh_cmt_interrupt; p->irqaction.dev_id = p; - p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; + p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \ + IRQF_IRQPOLL | IRQF_NOBALANCING; /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, cfg->clk); + p->clk = clk_get(&p->pdev->dev, "cmt_fck"); if (IS_ERR(p->clk)) { - pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk); - ret = PTR_ERR(p->clk); - goto err1; + dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); + p->clk = clk_get(&p->pdev->dev, cfg->clk); + if (IS_ERR(p->clk)) { + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; + } } if (resource_size(res) == 6) { @@ -629,17 +631,17 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) p->clear_bits = ~0xc000; } - ret = sh_cmt_register(p, cfg->name, + ret = sh_cmt_register(p, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); if (ret) { - pr_err("sh_cmt: registration failed\n"); + dev_err(&p->pdev->dev, "registration failed\n"); goto err1; } ret = setup_irq(irq, &p->irqaction); if (ret) { - pr_err("sh_cmt: failed to request irq %d\n", irq); + dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); goto err1; } @@ -654,11 +656,10 @@ err0: static int __devinit sh_cmt_probe(struct platform_device *pdev) { struct sh_cmt_priv *p = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (p) { - pr_info("sh_cmt: %s kept as earlytimer\n", cfg->name); + dev_info(&pdev->dev, "kept as earlytimer\n"); return 0; } diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 5fb78bfd73bb..ef7a5be8a09f 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -119,13 +119,12 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_priv *p, int start) static int sh_mtu2_enable(struct sh_mtu2_priv *p) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; int ret; /* enable clock */ ret = clk_enable(p->clk); if (ret) { - pr_err("sh_mtu2: cannot enable clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot enable clock\n"); return ret; } @@ -194,8 +193,7 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - pr_info("sh_mtu2: %s used for periodic clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for periodic clock events\n"); sh_mtu2_enable(p); break; case CLOCK_EVT_MODE_UNUSED: @@ -222,13 +220,13 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p, ced->cpumask = cpumask_of(0); ced->set_mode = sh_mtu2_clock_event_mode; - pr_info("sh_mtu2: %s used for clock events\n", ced->name); + dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); ret = setup_irq(p->irqaction.irq, &p->irqaction); if (ret) { - pr_err("sh_mtu2: failed to request irq %d\n", - p->irqaction.irq); + dev_err(&p->pdev->dev, "failed to request irq %d\n", + p->irqaction.irq); return; } } @@ -274,26 +272,32 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) /* map memory, let mapbase point to our channel */ p->mapbase = ioremap_nocache(res->start, resource_size(res)); if (p->mapbase == NULL) { - pr_err("sh_mtu2: failed to remap I/O memory\n"); + dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* setup data for setup_irq() (too early for request_irq()) */ - p->irqaction.name = cfg->name; + p->irqaction.name = dev_name(&p->pdev->dev); p->irqaction.handler = sh_mtu2_interrupt; p->irqaction.dev_id = p; p->irqaction.irq = irq; - p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; + p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \ + IRQF_IRQPOLL | IRQF_NOBALANCING; /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, cfg->clk); + p->clk = clk_get(&p->pdev->dev, "mtu2_fck"); if (IS_ERR(p->clk)) { - pr_err("sh_mtu2: cannot get clock \"%s\"\n", cfg->clk); - ret = PTR_ERR(p->clk); - goto err1; + dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); + p->clk = clk_get(&p->pdev->dev, cfg->clk); + if (IS_ERR(p->clk)) { + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; + } } - return sh_mtu2_register(p, cfg->name, cfg->clockevent_rating); + return sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev), + cfg->clockevent_rating); err1: iounmap(p->mapbase); err0: @@ -303,11 +307,10 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) static int __devinit sh_mtu2_probe(struct platform_device *pdev) { struct sh_mtu2_priv *p = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (p) { - pr_info("sh_mtu2: %s kept as earlytimer\n", cfg->name); + dev_info(&pdev->dev, "kept as earlytimer\n"); return 0; } diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index fc9ff1e5b770..8e44e14ec4c2 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -107,13 +107,12 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start) static int sh_tmu_enable(struct sh_tmu_priv *p) { - struct sh_timer_config *cfg = p->pdev->dev.platform_data; int ret; /* enable clock */ ret = clk_enable(p->clk); if (ret) { - pr_err("sh_tmu: cannot enable clock \"%s\"\n", cfg->clk); + dev_err(&p->pdev->dev, "cannot enable clock\n"); return ret; } @@ -229,7 +228,7 @@ static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, cs->disable = sh_tmu_clocksource_disable; cs->mask = CLOCKSOURCE_MASK(32); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; - pr_info("sh_tmu: %s used as clock source\n", cs->name); + dev_info(&p->pdev->dev, "used as clock source\n"); clocksource_register(cs); return 0; } @@ -277,13 +276,11 @@ static void sh_tmu_clock_event_mode(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - pr_info("sh_tmu: %s used for periodic clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for periodic clock events\n"); sh_tmu_clock_event_start(p, 1); break; case CLOCK_EVT_MODE_ONESHOT: - pr_info("sh_tmu: %s used for oneshot clock events\n", - ced->name); + dev_info(&p->pdev->dev, "used for oneshot clock events\n"); sh_tmu_clock_event_start(p, 0); break; case CLOCK_EVT_MODE_UNUSED: @@ -324,13 +321,13 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p, ced->set_next_event = sh_tmu_clock_event_next; ced->set_mode = sh_tmu_clock_event_mode; - pr_info("sh_tmu: %s used for clock events\n", ced->name); + dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); ret = setup_irq(p->irqaction.irq, &p->irqaction); if (ret) { - pr_err("sh_tmu: failed to request irq %d\n", - p->irqaction.irq); + dev_err(&p->pdev->dev, "failed to request irq %d\n", + p->irqaction.irq); return; } } @@ -379,26 +376,31 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) /* map memory, let mapbase point to our channel */ p->mapbase = ioremap_nocache(res->start, resource_size(res)); if (p->mapbase == NULL) { - pr_err("sh_tmu: failed to remap I/O memory\n"); + dev_err(&p->pdev->dev, "failed to remap I/O memory\n"); goto err0; } /* setup data for setup_irq() (too early for request_irq()) */ - p->irqaction.name = cfg->name; + p->irqaction.name = dev_name(&p->pdev->dev); p->irqaction.handler = sh_tmu_interrupt; p->irqaction.dev_id = p; p->irqaction.irq = irq; - p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; + p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \ + IRQF_IRQPOLL | IRQF_NOBALANCING; /* get hold of clock */ - p->clk = clk_get(&p->pdev->dev, cfg->clk); + p->clk = clk_get(&p->pdev->dev, "tmu_fck"); if (IS_ERR(p->clk)) { - pr_err("sh_tmu: cannot get clock \"%s\"\n", cfg->clk); - ret = PTR_ERR(p->clk); - goto err1; + dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); + p->clk = clk_get(&p->pdev->dev, cfg->clk); + if (IS_ERR(p->clk)) { + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; + } } - return sh_tmu_register(p, cfg->name, + return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating, cfg->clocksource_rating); err1: @@ -410,11 +412,10 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) static int __devinit sh_tmu_probe(struct platform_device *pdev) { struct sh_tmu_priv *p = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (p) { - pr_info("sh_tmu: %s kept as earlytimer\n", cfg->name); + dev_info(&pdev->dev, "kept as earlytimer\n"); return 0; } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 75d293eeb3ee..063b2184caf5 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -662,32 +662,20 @@ static ssize_t show_bios_limit(struct cpufreq_policy *policy, char *buf) return sprintf(buf, "%u\n", policy->cpuinfo.max_freq); } -#define define_one_ro(_name) \ -static struct freq_attr _name = \ -__ATTR(_name, 0444, show_##_name, NULL) - -#define define_one_ro0400(_name) \ -static struct freq_attr _name = \ -__ATTR(_name, 0400, show_##_name, NULL) - -#define define_one_rw(_name) \ -static struct freq_attr _name = \ -__ATTR(_name, 0644, show_##_name, store_##_name) - -define_one_ro0400(cpuinfo_cur_freq); -define_one_ro(cpuinfo_min_freq); -define_one_ro(cpuinfo_max_freq); -define_one_ro(cpuinfo_transition_latency); -define_one_ro(scaling_available_governors); -define_one_ro(scaling_driver); -define_one_ro(scaling_cur_freq); -define_one_ro(bios_limit); -define_one_ro(related_cpus); -define_one_ro(affected_cpus); -define_one_rw(scaling_min_freq); -define_one_rw(scaling_max_freq); -define_one_rw(scaling_governor); -define_one_rw(scaling_setspeed); +cpufreq_freq_attr_ro_perm(cpuinfo_cur_freq, 0400); +cpufreq_freq_attr_ro(cpuinfo_min_freq); +cpufreq_freq_attr_ro(cpuinfo_max_freq); +cpufreq_freq_attr_ro(cpuinfo_transition_latency); +cpufreq_freq_attr_ro(scaling_available_governors); +cpufreq_freq_attr_ro(scaling_driver); +cpufreq_freq_attr_ro(scaling_cur_freq); +cpufreq_freq_attr_ro(bios_limit); +cpufreq_freq_attr_ro(related_cpus); +cpufreq_freq_attr_ro(affected_cpus); +cpufreq_freq_attr_rw(scaling_min_freq); +cpufreq_freq_attr_rw(scaling_max_freq); +cpufreq_freq_attr_rw(scaling_governor); +cpufreq_freq_attr_rw(scaling_setspeed); static struct attribute *default_attrs[] = { &cpuinfo_min_freq.attr, diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 3a147874a465..526bfbf69611 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -178,12 +178,8 @@ static ssize_t show_sampling_rate_min(struct kobject *kobj, return sprintf(buf, "%u\n", min_sampling_rate); } -#define define_one_ro(_name) \ -static struct global_attr _name = \ -__ATTR(_name, 0444, show_##_name, NULL) - -define_one_ro(sampling_rate_max); -define_one_ro(sampling_rate_min); +define_one_global_ro(sampling_rate_max); +define_one_global_ro(sampling_rate_min); /* cpufreq_conservative Governor Tunables */ #define show_one(file_name, object) \ @@ -221,12 +217,8 @@ show_one_old(freq_step); show_one_old(sampling_rate_min); show_one_old(sampling_rate_max); -#define define_one_ro_old(object, _name) \ -static struct freq_attr object = \ -__ATTR(_name, 0444, show_##_name##_old, NULL) - -define_one_ro_old(sampling_rate_min_old, sampling_rate_min); -define_one_ro_old(sampling_rate_max_old, sampling_rate_max); +cpufreq_freq_attr_ro_old(sampling_rate_min); +cpufreq_freq_attr_ro_old(sampling_rate_max); /*** delete after deprecation time ***/ @@ -364,16 +356,12 @@ static ssize_t store_freq_step(struct kobject *a, struct attribute *b, return count; } -#define define_one_rw(_name) \ -static struct global_attr _name = \ -__ATTR(_name, 0644, show_##_name, store_##_name) - -define_one_rw(sampling_rate); -define_one_rw(sampling_down_factor); -define_one_rw(up_threshold); -define_one_rw(down_threshold); -define_one_rw(ignore_nice_load); -define_one_rw(freq_step); +define_one_global_rw(sampling_rate); +define_one_global_rw(sampling_down_factor); +define_one_global_rw(up_threshold); +define_one_global_rw(down_threshold); +define_one_global_rw(ignore_nice_load); +define_one_global_rw(freq_step); static struct attribute *dbs_attributes[] = { &sampling_rate_max.attr, @@ -409,16 +397,12 @@ write_one_old(down_threshold); write_one_old(ignore_nice_load); write_one_old(freq_step); -#define define_one_rw_old(object, _name) \ -static struct freq_attr object = \ -__ATTR(_name, 0644, show_##_name##_old, store_##_name##_old) - -define_one_rw_old(sampling_rate_old, sampling_rate); -define_one_rw_old(sampling_down_factor_old, sampling_down_factor); -define_one_rw_old(up_threshold_old, up_threshold); -define_one_rw_old(down_threshold_old, down_threshold); -define_one_rw_old(ignore_nice_load_old, ignore_nice_load); -define_one_rw_old(freq_step_old, freq_step); +cpufreq_freq_attr_rw_old(sampling_rate); +cpufreq_freq_attr_rw_old(sampling_down_factor); +cpufreq_freq_attr_rw_old(up_threshold); +cpufreq_freq_attr_rw_old(down_threshold); +cpufreq_freq_attr_rw_old(ignore_nice_load); +cpufreq_freq_attr_rw_old(freq_step); static struct attribute *dbs_attributes_old[] = { &sampling_rate_max_old.attr, diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index bd444dc93cf2..e1314212d8d4 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -73,6 +73,7 @@ enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE}; struct cpu_dbs_info_s { cputime64_t prev_cpu_idle; + cputime64_t prev_cpu_iowait; cputime64_t prev_cpu_wall; cputime64_t prev_cpu_nice; struct cpufreq_policy *cur_policy; @@ -108,6 +109,7 @@ static struct dbs_tuners { unsigned int down_differential; unsigned int ignore_nice; unsigned int powersave_bias; + unsigned int io_is_busy; } dbs_tuners_ins = { .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, .down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL, @@ -148,6 +150,16 @@ static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) return idle_time; } +static inline cputime64_t get_cpu_iowait_time(unsigned int cpu, cputime64_t *wall) +{ + u64 iowait_time = get_cpu_iowait_time_us(cpu, wall); + + if (iowait_time == -1ULL) + return 0; + + return iowait_time; +} + /* * Find right freq to be set now with powersave_bias on. * Returns the freq_hi to be used right now and will set freq_hi_jiffies, @@ -234,12 +246,8 @@ static ssize_t show_sampling_rate_min(struct kobject *kobj, return sprintf(buf, "%u\n", min_sampling_rate); } -#define define_one_ro(_name) \ -static struct global_attr _name = \ -__ATTR(_name, 0444, show_##_name, NULL) - -define_one_ro(sampling_rate_max); -define_one_ro(sampling_rate_min); +define_one_global_ro(sampling_rate_max); +define_one_global_ro(sampling_rate_min); /* cpufreq_ondemand Governor Tunables */ #define show_one(file_name, object) \ @@ -249,6 +257,7 @@ static ssize_t show_##file_name \ return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ } show_one(sampling_rate, sampling_rate); +show_one(io_is_busy, io_is_busy); show_one(up_threshold, up_threshold); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); @@ -274,12 +283,8 @@ show_one_old(powersave_bias); show_one_old(sampling_rate_min); show_one_old(sampling_rate_max); -#define define_one_ro_old(object, _name) \ -static struct freq_attr object = \ -__ATTR(_name, 0444, show_##_name##_old, NULL) - -define_one_ro_old(sampling_rate_min_old, sampling_rate_min); -define_one_ro_old(sampling_rate_max_old, sampling_rate_max); +cpufreq_freq_attr_ro_old(sampling_rate_min); +cpufreq_freq_attr_ro_old(sampling_rate_max); /*** delete after deprecation time ***/ @@ -299,6 +304,23 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, return count; } +static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + mutex_lock(&dbs_mutex); + dbs_tuners_ins.io_is_busy = !!input; + mutex_unlock(&dbs_mutex); + + return count; +} + static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -376,14 +398,11 @@ static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, return count; } -#define define_one_rw(_name) \ -static struct global_attr _name = \ -__ATTR(_name, 0644, show_##_name, store_##_name) - -define_one_rw(sampling_rate); -define_one_rw(up_threshold); -define_one_rw(ignore_nice_load); -define_one_rw(powersave_bias); +define_one_global_rw(sampling_rate); +define_one_global_rw(io_is_busy); +define_one_global_rw(up_threshold); +define_one_global_rw(ignore_nice_load); +define_one_global_rw(powersave_bias); static struct attribute *dbs_attributes[] = { &sampling_rate_max.attr, @@ -392,6 +411,7 @@ static struct attribute *dbs_attributes[] = { &up_threshold.attr, &ignore_nice_load.attr, &powersave_bias.attr, + &io_is_busy.attr, NULL }; @@ -415,14 +435,10 @@ write_one_old(up_threshold); write_one_old(ignore_nice_load); write_one_old(powersave_bias); -#define define_one_rw_old(object, _name) \ -static struct freq_attr object = \ -__ATTR(_name, 0644, show_##_name##_old, store_##_name##_old) - -define_one_rw_old(sampling_rate_old, sampling_rate); -define_one_rw_old(up_threshold_old, up_threshold); -define_one_rw_old(ignore_nice_load_old, ignore_nice_load); -define_one_rw_old(powersave_bias_old, powersave_bias); +cpufreq_freq_attr_rw_old(sampling_rate); +cpufreq_freq_attr_rw_old(up_threshold); +cpufreq_freq_attr_rw_old(ignore_nice_load); +cpufreq_freq_attr_rw_old(powersave_bias); static struct attribute *dbs_attributes_old[] = { &sampling_rate_max_old.attr, @@ -470,14 +486,15 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) for_each_cpu(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; - cputime64_t cur_wall_time, cur_idle_time; - unsigned int idle_time, wall_time; + cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; + unsigned int idle_time, wall_time, iowait_time; unsigned int load, load_freq; int freq_avg; j_dbs_info = &per_cpu(od_cpu_dbs_info, j); cur_idle_time = get_cpu_idle_time(j, &cur_wall_time); + cur_iowait_time = get_cpu_iowait_time(j, &cur_wall_time); wall_time = (unsigned int) cputime64_sub(cur_wall_time, j_dbs_info->prev_cpu_wall); @@ -487,6 +504,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) j_dbs_info->prev_cpu_idle); j_dbs_info->prev_cpu_idle = cur_idle_time; + iowait_time = (unsigned int) cputime64_sub(cur_iowait_time, + j_dbs_info->prev_cpu_iowait); + j_dbs_info->prev_cpu_iowait = cur_iowait_time; + if (dbs_tuners_ins.ignore_nice) { cputime64_t cur_nice; unsigned long cur_nice_jiffies; @@ -504,6 +525,16 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) idle_time += jiffies_to_usecs(cur_nice_jiffies); } + /* + * For the purpose of ondemand, waiting for disk IO is an + * indication that you're performance critical, and not that + * the system is actually idle. So subtract the iowait time + * from the cpu idle time. + */ + + if (dbs_tuners_ins.io_is_busy && idle_time >= iowait_time) + idle_time -= iowait_time; + if (unlikely(!wall_time || wall_time < idle_time)) continue; @@ -617,6 +648,29 @@ static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) cancel_delayed_work_sync(&dbs_info->work); } +/* + * Not all CPUs want IO time to be accounted as busy; this dependson how + * efficient idling at a higher frequency/voltage is. + * Pavel Machek says this is not so for various generations of AMD and old + * Intel systems. + * Mike Chan (androidlcom) calis this is also not true for ARM. + * Because of this, whitelist specific known (series) of CPUs by default, and + * leave all others up to the user. + */ +static int should_io_be_busy(void) +{ +#if defined(CONFIG_X86) + /* + * For Intel, Core 2 (model 15) andl later have an efficient idle. + */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && + boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model >= 15) + return 1; +#endif + return 0; +} + static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event) { @@ -679,6 +733,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, dbs_tuners_ins.sampling_rate = max(min_sampling_rate, latency * LATENCY_MULTIPLIER); + dbs_tuners_ins.io_is_busy = should_io_be_busy(); } mutex_unlock(&dbs_mutex); diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index 1c1ceb4f218f..12c98900dcf8 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c @@ -67,7 +67,7 @@ static int ladder_select_state(struct cpuidle_device *dev) struct ladder_device *ldev = &__get_cpu_var(ladder_devices); struct ladder_device_state *last_state; int last_residency, last_idx = ldev->last_state_idx; - int latency_req = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY); + int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); /* Special case when user has set very strict latency requirement */ if (unlikely(latency_req == 0)) { diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index f8e57c6303f2..b81ad9c731ae 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -182,7 +182,7 @@ static u64 div_round64(u64 dividend, u32 divisor) static int menu_select(struct cpuidle_device *dev) { struct menu_device *data = &__get_cpu_var(menu_devices); - int latency_req = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY); + int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); int i; int multiplier; diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 6f25a20de99f..323afef77802 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -26,8 +26,7 @@ #include <linux/dma-mapping.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> - -#include <asm/dmaengine.h> +#include <linux/sh_dma.h> #include "shdma.h" @@ -45,7 +44,7 @@ enum sh_dmae_desc_status { #define LOG2_DEFAULT_XFER_SIZE 2 /* A bitmask with bits enough for enum sh_dmae_slave_chan_id */ -static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SHDMA_SLAVE_NUMBER)]; +static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)]; static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all); @@ -190,7 +189,7 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) struct sh_dmae_device *shdev = container_of(sh_chan->common.device, struct sh_dmae_device, common); struct sh_dmae_pdata *pdata = shdev->pdata; - struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id]; + const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id]; u16 __iomem *addr = shdev->dmars + chan_pdata->dmars / sizeof(u16); int shift = chan_pdata->dmars_bit; @@ -266,8 +265,8 @@ static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan) return NULL; } -static struct sh_dmae_slave_config *sh_dmae_find_slave( - struct sh_dmae_chan *sh_chan, enum sh_dmae_slave_chan_id slave_id) +static const struct sh_dmae_slave_config *sh_dmae_find_slave( + struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param) { struct dma_device *dma_dev = sh_chan->common.device; struct sh_dmae_device *shdev = container_of(dma_dev, @@ -275,11 +274,11 @@ static struct sh_dmae_slave_config *sh_dmae_find_slave( struct sh_dmae_pdata *pdata = shdev->pdata; int i; - if ((unsigned)slave_id >= SHDMA_SLAVE_NUMBER) + if (param->slave_id >= SH_DMA_SLAVE_NUMBER) return NULL; for (i = 0; i < pdata->slave_num; i++) - if (pdata->slave[i].slave_id == slave_id) + if (pdata->slave[i].slave_id == param->slave_id) return pdata->slave + i; return NULL; @@ -299,9 +298,9 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) * never runs concurrently with itself or free_chan_resources. */ if (param) { - struct sh_dmae_slave_config *cfg; + const struct sh_dmae_slave_config *cfg; - cfg = sh_dmae_find_slave(sh_chan, param->slave_id); + cfg = sh_dmae_find_slave(sh_chan, param); if (!cfg) { ret = -EINVAL; goto efindslave; @@ -574,12 +573,14 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg( { struct sh_dmae_slave *param; struct sh_dmae_chan *sh_chan; + dma_addr_t slave_addr; if (!chan) return NULL; sh_chan = to_sh_chan(chan); param = chan->private; + slave_addr = param->config->addr; /* Someone calling slave DMA on a public channel? */ if (!param || !sg_len) { @@ -592,7 +593,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg( * if (param != NULL), this is a successfully requested slave channel, * therefore param->config != NULL too. */ - return sh_dmae_prep_sg(sh_chan, sgl, sg_len, ¶m->config->addr, + return sh_dmae_prep_sg(sh_chan, sgl, sg_len, &slave_addr, direction, flags); } @@ -873,7 +874,7 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, int irq, unsigned long flags) { int err; - struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id]; + const struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id]; struct platform_device *pdev = to_platform_device(shdev->common.dev); struct sh_dmae_chan *new_sh_chan; diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index 153609a1e96c..4021275a0a43 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -17,8 +17,8 @@ #include <linux/interrupt.h> #include <linux/list.h> -#include <asm/dmaengine.h> - +#define SH_DMAC_MAX_CHANNELS 6 +#define SH_DMA_SLAVE_NUMBER 256 #define SH_DMA_TCR_MAX 0x00FFFFFF /* 16MB */ struct device; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 94b16e0340ae..a3b083a7403a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1351,7 +1351,7 @@ static void bus_reset_tasklet(unsigned long data) * was set up before this reset, the old one is now no longer * in use and we can free it. Update the config rom pointers * to point to the current config rom and clear the - * next_config_rom pointer so a new udpate can take place. + * next_config_rom pointer so a new update can take place. */ if (ohci->next_config_rom != NULL) { diff --git a/drivers/gpio/wm8994-gpio.c b/drivers/gpio/wm8994-gpio.c index 7607cc61e1dd..2ac9a16d3daa 100644 --- a/drivers/gpio/wm8994-gpio.c +++ b/drivers/gpio/wm8994-gpio.c @@ -81,6 +81,18 @@ static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value); } +static int wm8994_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); + struct wm8994 *wm8994 = wm8994_gpio->wm8994; + + if (!wm8994->irq_base) + return -EINVAL; + + return wm8994->irq_base + offset; +} + + #ifdef CONFIG_DEBUG_FS static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index f7ba82ebf65a..2092e7bb788f 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -961,7 +961,7 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) dma->buflist[i + dma->buf_count] = &entry->buflist[i]; } - /* No allocations failed, so now we can replace the orginal pagelist + /* No allocations failed, so now we can replace the original pagelist * with the new one. */ if (dma->page_count) { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c7502b6b1600..f27e3703a716 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -905,9 +905,9 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, memset(best_clock, 0, sizeof(*best_clock)); max_n = limit->n.max; - /* based on hardware requriment prefer smaller n to precision */ + /* based on hardware requirement, prefer smaller n to precision */ for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { - /* based on hardware requirment prefere larger m1,m2 */ + /* based on hardware requirement, prefere larger m1,m2 */ for (clock.m1 = limit->m1.max; clock.m1 >= limit->m1.min; clock.m1--) { for (clock.m2 = limit->m2.max; diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index 27e2c715be11..5319d9e2f7ba 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -3780,7 +3780,7 @@ typedef struct _ATOM_ASIC_SS_ASSIGNMENT UCHAR ucReserved[2]; }ATOM_ASIC_SS_ASSIGNMENT; -//Define ucClockIndication, SW uses the IDs below to search if the SS is requried/enabled on a clock branch/signal type. +//Define ucClockIndication, SW uses the IDs below to search if the SS is required/enabled on a clock branch/signal type. //SS is not required or enabled if a match is not found. #define ASIC_INTERNAL_MEMORY_SS 1 #define ASIC_INTERNAL_ENGINE_SS 2 @@ -5895,7 +5895,7 @@ typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO UCHAR ucPadding; // For proper alignment and size. USHORT usVDDC; // For the 780, use: None, Low, High, Variable UCHAR ucMaxHTLinkWidth; // From SBIOS - {2, 4, 8, 16} - UCHAR ucMinHTLinkWidth; // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requriement. + UCHAR ucMinHTLinkWidth; // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requirement. USHORT usHTLinkFreq; // See definition ATOM_PPLIB_RS780_HTLINKFREQ_xxx or in MHz(>=200). ULONG ulFlags; } ATOM_PPLIB_RS780_CLOCK_INFO; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 9c6170cd9aac..87ab0568bb0e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -564,7 +564,7 @@ config I2C_STU300 config I2C_VERSATILE tristate "ARM Versatile/Realview I2C bus support" - depends on ARCH_VERSATILE || ARCH_REALVIEW + depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS select I2C_ALGOBIT help Say yes if you want to support the I2C serial bus on ARMs Versatile diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index f1e14dd590c9..fb26e5c67515 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -25,8 +25,6 @@ #include <asm/portmux.h> #include <asm/irq.h> -#define POLL_TIMEOUT (2 * HZ) - /* SMBus mode*/ #define TWI_I2C_MODE_STANDARD 1 #define TWI_I2C_MODE_STANDARDSUB 2 @@ -44,8 +42,6 @@ struct bfin_twi_iface { int cur_mode; int manual_stop; int result; - int timeout_count; - struct timer_list timeout_timer; struct i2c_adapter adap; struct completion complete; struct i2c_msg *pmsg; @@ -85,14 +81,15 @@ static const u16 pin_req[2][3] = { {P_TWI1_SCL, P_TWI1_SDA, 0}, }; -static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) +static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, + unsigned short twi_int_status) { - unsigned short twi_int_status = read_INT_STAT(iface); unsigned short mast_stat = read_MASTER_STAT(iface); if (twi_int_status & XMTSERV) { /* Transmit next data */ if (iface->writeNum > 0) { + SSYNC(); write_XMT_DATA8(iface, *(iface->transPtr++)); iface->writeNum--; } @@ -114,10 +111,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) write_MASTER_CTL(iface, (read_MASTER_CTL(iface) | RSTART) & ~MDIR); } - SSYNC(); - /* Clear status */ - write_INT_STAT(iface, XMTSERV); - SSYNC(); } if (twi_int_status & RCVSERV) { if (iface->readNum > 0) { @@ -139,7 +132,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) } else if (iface->manual_stop) { write_MASTER_CTL(iface, read_MASTER_CTL(iface) | STOP); - SSYNC(); } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && iface->cur_msg + 1 < iface->msg_num) { if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) @@ -148,44 +140,37 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) else write_MASTER_CTL(iface, (read_MASTER_CTL(iface) | RSTART) & ~MDIR); - SSYNC(); } - /* Clear interrupt source */ - write_INT_STAT(iface, RCVSERV); - SSYNC(); } if (twi_int_status & MERR) { - write_INT_STAT(iface, MERR); write_INT_MASK(iface, 0); write_MASTER_STAT(iface, 0x3e); write_MASTER_CTL(iface, 0); - SSYNC(); iface->result = -EIO; - /* if both err and complete int stats are set, return proper - * results. + + if (mast_stat & LOSTARB) + dev_dbg(&iface->adap.dev, "Lost Arbitration\n"); + if (mast_stat & ANAK) + dev_dbg(&iface->adap.dev, "Address Not Acknowledged\n"); + if (mast_stat & DNAK) + dev_dbg(&iface->adap.dev, "Data Not Acknowledged\n"); + if (mast_stat & BUFRDERR) + dev_dbg(&iface->adap.dev, "Buffer Read Error\n"); + if (mast_stat & BUFWRERR) + dev_dbg(&iface->adap.dev, "Buffer Write Error\n"); + + /* If it is a quick transfer, only address without data, + * not an err, return 1. */ - if (twi_int_status & MCOMP) { - write_INT_STAT(iface, MCOMP); - write_INT_MASK(iface, 0); - write_MASTER_CTL(iface, 0); - SSYNC(); - /* If it is a quick transfer, only address bug no data, - * not an err, return 1. - */ - if (iface->writeNum == 0 && (mast_stat & BUFRDERR)) - iface->result = 1; - /* If address not acknowledged return -1, - * else return 0. - */ - else if (!(mast_stat & ANAK)) - iface->result = 0; - } + if (iface->cur_mode == TWI_I2C_MODE_STANDARD && + iface->transPtr == NULL && + (twi_int_status & MCOMP) && (mast_stat & DNAK)) + iface->result = 1; + complete(&iface->complete); return; } if (twi_int_status & MCOMP) { - write_INT_STAT(iface, MCOMP); - SSYNC(); if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { if (iface->readNum == 0) { /* set the read number to 1 and ask for manual @@ -207,7 +192,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) /* remove restart bit and enable master receive */ write_MASTER_CTL(iface, read_MASTER_CTL(iface) & ~RSTART); - SSYNC(); } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && iface->cur_msg+1 < iface->msg_num) { iface->cur_msg++; @@ -226,7 +210,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) write_XMT_DATA8(iface, *(iface->transPtr++)); iface->writeNum--; - SSYNC(); } } @@ -244,15 +227,13 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) /* remove restart bit and enable master receive */ write_MASTER_CTL(iface, read_MASTER_CTL(iface) & ~RSTART); - SSYNC(); } else { iface->result = 1; write_INT_MASK(iface, 0); write_MASTER_CTL(iface, 0); - SSYNC(); - complete(&iface->complete); } } + complete(&iface->complete); } /* Interrupt handler */ @@ -260,38 +241,26 @@ static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id) { struct bfin_twi_iface *iface = dev_id; unsigned long flags; + unsigned short twi_int_status; spin_lock_irqsave(&iface->lock, flags); - del_timer(&iface->timeout_timer); - bfin_twi_handle_interrupt(iface); - spin_unlock_irqrestore(&iface->lock, flags); - return IRQ_HANDLED; -} - -static void bfin_twi_timeout(unsigned long data) -{ - struct bfin_twi_iface *iface = (struct bfin_twi_iface *)data; - unsigned long flags; - - spin_lock_irqsave(&iface->lock, flags); - bfin_twi_handle_interrupt(iface); - if (iface->result == 0) { - iface->timeout_count--; - if (iface->timeout_count > 0) { - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); - } else { - iface->result = -1; - complete(&iface->complete); - } + while (1) { + twi_int_status = read_INT_STAT(iface); + if (!twi_int_status) + break; + /* Clear interrupt status */ + write_INT_STAT(iface, twi_int_status); + bfin_twi_handle_interrupt(iface, twi_int_status); + SSYNC(); } spin_unlock_irqrestore(&iface->lock, flags); + return IRQ_HANDLED; } /* - * Generic i2c master transfer entrypoint + * One i2c master transfer */ -static int bfin_twi_master_xfer(struct i2c_adapter *adap, +static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct bfin_twi_iface *iface = adap->algo_data; @@ -319,7 +288,6 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap, iface->transPtr = pmsg->buf; iface->writeNum = iface->readNum = pmsg->len; iface->result = 0; - iface->timeout_count = 10; init_completion(&(iface->complete)); /* Set Transmit device address */ write_MASTER_ADDR(iface, pmsg->addr); @@ -358,30 +326,41 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap, iface->manual_stop = 1; } - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); - /* Master enable */ write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); SSYNC(); - wait_for_completion(&iface->complete); - - rc = iface->result; + while (!iface->result) { + if (!wait_for_completion_timeout(&iface->complete, + adap->timeout)) { + iface->result = -1; + dev_err(&adap->dev, "master transfer timeout\n"); + } + } - if (rc == 1) - return num; + if (iface->result == 1) + rc = iface->cur_msg + 1; else - return rc; + rc = iface->result; + + return rc; } /* - * SMBus type transfer entrypoint + * Generic i2c master transfer entrypoint */ +static int bfin_twi_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return bfin_twi_do_master_xfer(adap, msgs, num); +} -int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, +/* + * One I2C SMBus transfer + */ +int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { @@ -469,7 +448,6 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, iface->manual_stop = 0; iface->read_write = read_write; iface->command = command; - iface->timeout_count = 10; init_completion(&(iface->complete)); /* FIFO Initiation. Data in FIFO should be discarded before @@ -486,9 +464,6 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, write_MASTER_ADDR(iface, addr); SSYNC(); - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); - switch (iface->cur_mode) { case TWI_I2C_MODE_STANDARDSUB: write_XMT_DATA8(iface, iface->command); @@ -550,10 +525,8 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, else if (iface->readNum > 255) { write_MASTER_CTL(iface, 0xff << 6); iface->manual_stop = 1; - } else { - del_timer(&iface->timeout_timer); + } else break; - } } } write_INT_MASK(iface, MCOMP | MERR | @@ -569,7 +542,13 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, } SSYNC(); - wait_for_completion(&iface->complete); + while (!iface->result) { + if (!wait_for_completion_timeout(&iface->complete, + adap->timeout)) { + iface->result = -1; + dev_err(&adap->dev, "smbus transfer timeout\n"); + } + } rc = (iface->result >= 0) ? 0 : -1; @@ -577,6 +556,17 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, } /* + * Generic I2C SMBus transfer entrypoint + */ +int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + return bfin_twi_do_smbus_xfer(adap, addr, flags, + read_write, command, size, data); +} + +/* * Return what the adapter supports */ static u32 bfin_twi_functionality(struct i2c_adapter *adap) @@ -667,10 +657,6 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) goto out_error_no_irq; } - init_timer(&(iface->timeout_timer)); - iface->timeout_timer.function = bfin_twi_timeout; - iface->timeout_timer.data = (unsigned long)iface; - p_adap = &iface->adap; p_adap->nr = pdev->id; strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name)); @@ -678,6 +664,8 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) p_adap->algo_data = iface; p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; p_adap->dev.parent = &pdev->dev; + p_adap->timeout = 5 * HZ; + p_adap->retries = 3; rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi"); if (rc) { diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 9c2e10082b79..16948db38973 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -441,7 +441,7 @@ static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm) init_waitqueue_head(&cpm->i2c_wait); cpm->irq = of_irq_to_resource(ofdev->node, 0, NULL); - if (cpm->irq == NO_IRQ) + if (!cpm->irq) return -EINVAL; /* Install interrupt handler. */ diff --git a/drivers/i2c/busses/i2c-highlander.c b/drivers/i2c/busses/i2c-highlander.c index ce87a902c94d..3df1bc80f37a 100644 --- a/drivers/i2c/busses/i2c-highlander.c +++ b/drivers/i2c/busses/i2c-highlander.c @@ -282,7 +282,6 @@ static int highlander_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, union i2c_smbus_data *data) { struct highlander_i2c_dev *dev = i2c_get_adapdata(adap); - int read = read_write & I2C_SMBUS_READ; u16 tmp; init_completion(&dev->cmd_complete); @@ -337,11 +336,11 @@ static int highlander_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, highlander_i2c_done(dev); /* Set slave address */ - iowrite16((addr << 1) | read, dev->base + SMSMADR); + iowrite16((addr << 1) | read_write, dev->base + SMSMADR); highlander_i2c_command(dev, command, dev->buf_len); - if (read) + if (read_write == I2C_SMBUS_READ) return highlander_i2c_read(dev); else return highlander_i2c_write(dev); diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index b1bc6e277d2a..2bef534cbff1 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -668,12 +668,12 @@ static int __devinit iic_request_irq(struct of_device *ofdev, int irq; if (iic_force_poll) - return NO_IRQ; + return 0; irq = irq_of_parse_and_map(np, 0); - if (irq == NO_IRQ) { + if (!irq) { dev_err(&ofdev->dev, "irq_of_parse_and_map failed\n"); - return NO_IRQ; + return 0; } /* Disable interrupts until we finish initialization, assumes @@ -683,7 +683,7 @@ static int __devinit iic_request_irq(struct of_device *ofdev, if (request_irq(irq, iic_handler, 0, "IBM IIC", dev)) { dev_err(&ofdev->dev, "request_irq %d failed\n", irq); /* Fallback to the polling mode */ - return NO_IRQ; + return 0; } return irq; @@ -719,7 +719,7 @@ static int __devinit iic_probe(struct of_device *ofdev, init_waitqueue_head(&dev->wq); dev->irq = iic_request_irq(ofdev, dev); - if (dev->irq == NO_IRQ) + if (!dev->irq) dev_warn(&ofdev->dev, "using polling mode\n"); /* Board specific settings */ @@ -766,7 +766,7 @@ static int __devinit iic_probe(struct of_device *ofdev, return 0; error_cleanup: - if (dev->irq != NO_IRQ) { + if (dev->irq) { iic_interrupt_mode(dev, 0); free_irq(dev->irq, dev); } @@ -790,7 +790,7 @@ static int __devexit iic_remove(struct of_device *ofdev) i2c_del_adapter(&dev->adap); - if (dev->irq != NO_IRQ) { + if (dev->irq) { iic_interrupt_mode(dev, 0); free_irq(dev->irq, dev); } diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index f1321f763789..e86cef300c7d 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -118,7 +118,7 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) u32 x; int result = 0; - if (i2c->irq == NO_IRQ) { + if (!i2c->irq) { while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) { schedule(); if (time_after(jiffies, orig_jiffies + timeout)) { @@ -568,7 +568,7 @@ static int __devinit fsl_i2c_probe(struct of_device *op, } i2c->irq = irq_of_parse_and_map(op->node, 0); - if (i2c->irq != NO_IRQ) { /* i2c->irq = NO_IRQ implies polling */ + if (i2c->irq) { /* no i2c->irq implies polling */ result = request_irq(i2c->irq, mpc_i2c_isr, IRQF_SHARED, "i2c-mpc", i2c); if (result < 0) { @@ -627,7 +627,7 @@ static int __devexit fsl_i2c_remove(struct of_device *op) i2c_del_adapter(&i2c->adap); dev_set_drvdata(&op->dev, NULL); - if (i2c->irq != NO_IRQ) + if (i2c->irq) free_irq(i2c->irq, i2c); irq_dispose_mapping(i2c->irq); diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index a4f8d33fa389..73de8ade10b1 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -704,7 +704,8 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) case I2C_IT_MTD: case I2C_IT_MTDWS: if (dev->cli.operation == I2C_READ) { - while (!readl(dev->virtbase + I2C_RISR) & I2C_IT_RXFE) { + while (!(readl(dev->virtbase + I2C_RISR) + & I2C_IT_RXFE)) { if (dev->cli.count == 0) break; *dev->cli.buffer = @@ -914,6 +915,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev) static int __devexit nmk_i2c_remove(struct platform_device *pdev) { + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct nmk_i2c_dev *dev = platform_get_drvdata(pdev); i2c_del_adapter(&dev->adap); @@ -924,6 +926,8 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev) i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE); free_irq(dev->irq, dev); iounmap(dev->virtbase); + if (res) + release_mem_region(res->start, resource_size(res)); clk_disable(dev->clk); clk_put(dev->clk); platform_set_drvdata(pdev, NULL); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 389ac6032a7b..7674efb55378 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -38,6 +38,7 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/slab.h> +#include <linux/i2c-omap.h> /* I2C controller revisions */ #define OMAP_I2C_REV_2 0x20 @@ -45,29 +46,37 @@ /* I2C controller revisions present on specific hardware */ #define OMAP_I2C_REV_ON_2430 0x36 #define OMAP_I2C_REV_ON_3430 0x3C +#define OMAP_I2C_REV_ON_4430 0x40 /* timeout waiting for the controller to respond */ #define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000)) -#define OMAP_I2C_REV_REG 0x00 -#define OMAP_I2C_IE_REG 0x01 -#define OMAP_I2C_STAT_REG 0x02 -#define OMAP_I2C_IV_REG 0x03 /* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */ -#define OMAP_I2C_WE_REG 0x03 -#define OMAP_I2C_SYSS_REG 0x04 -#define OMAP_I2C_BUF_REG 0x05 -#define OMAP_I2C_CNT_REG 0x06 -#define OMAP_I2C_DATA_REG 0x07 -#define OMAP_I2C_SYSC_REG 0x08 -#define OMAP_I2C_CON_REG 0x09 -#define OMAP_I2C_OA_REG 0x0a -#define OMAP_I2C_SA_REG 0x0b -#define OMAP_I2C_PSC_REG 0x0c -#define OMAP_I2C_SCLL_REG 0x0d -#define OMAP_I2C_SCLH_REG 0x0e -#define OMAP_I2C_SYSTEST_REG 0x0f -#define OMAP_I2C_BUFSTAT_REG 0x10 +enum { + OMAP_I2C_REV_REG = 0, + OMAP_I2C_IE_REG, + OMAP_I2C_STAT_REG, + OMAP_I2C_IV_REG, + OMAP_I2C_WE_REG, + OMAP_I2C_SYSS_REG, + OMAP_I2C_BUF_REG, + OMAP_I2C_CNT_REG, + OMAP_I2C_DATA_REG, + OMAP_I2C_SYSC_REG, + OMAP_I2C_CON_REG, + OMAP_I2C_OA_REG, + OMAP_I2C_SA_REG, + OMAP_I2C_PSC_REG, + OMAP_I2C_SCLL_REG, + OMAP_I2C_SCLH_REG, + OMAP_I2C_SYSTEST_REG, + OMAP_I2C_BUFSTAT_REG, + OMAP_I2C_REVNB_LO, + OMAP_I2C_REVNB_HI, + OMAP_I2C_IRQSTATUS_RAW, + OMAP_I2C_IRQENABLE_SET, + OMAP_I2C_IRQENABLE_CLR, +}; /* I2C Interrupt Enable Register (OMAP_I2C_IE): */ #define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer drain int enable */ @@ -157,6 +166,9 @@ #define SYSC_IDLEMODE_SMART 0x2 #define SYSC_CLOCKACTIVITY_FCLK 0x2 +/* Errata definitions */ +#define I2C_OMAP_ERRATA_I207 (1 << 0) +#define I2C_OMAP3_1P153 (1 << 1) struct omap_i2c_dev { struct device *dev; @@ -167,9 +179,13 @@ struct omap_i2c_dev { struct clk *fclk; /* Functional clock */ struct completion cmd_complete; struct resource *ioarea; + u32 latency; /* maximum mpu wkup latency */ + void (*set_mpu_wkup_lat)(struct device *dev, + long latency); u32 speed; /* Speed of bus in Khz */ u16 cmd_err; u8 *buf; + u8 *regs; size_t buf_len; struct i2c_adapter adapter; u8 fifo_size; /* use as flag and value @@ -186,17 +202,67 @@ struct omap_i2c_dev { u16 bufstate; u16 syscstate; u16 westate; + u16 errata; +}; + +const static u8 reg_map[] = { + [OMAP_I2C_REV_REG] = 0x00, + [OMAP_I2C_IE_REG] = 0x01, + [OMAP_I2C_STAT_REG] = 0x02, + [OMAP_I2C_IV_REG] = 0x03, + [OMAP_I2C_WE_REG] = 0x03, + [OMAP_I2C_SYSS_REG] = 0x04, + [OMAP_I2C_BUF_REG] = 0x05, + [OMAP_I2C_CNT_REG] = 0x06, + [OMAP_I2C_DATA_REG] = 0x07, + [OMAP_I2C_SYSC_REG] = 0x08, + [OMAP_I2C_CON_REG] = 0x09, + [OMAP_I2C_OA_REG] = 0x0a, + [OMAP_I2C_SA_REG] = 0x0b, + [OMAP_I2C_PSC_REG] = 0x0c, + [OMAP_I2C_SCLL_REG] = 0x0d, + [OMAP_I2C_SCLH_REG] = 0x0e, + [OMAP_I2C_SYSTEST_REG] = 0x0f, + [OMAP_I2C_BUFSTAT_REG] = 0x10, +}; + +const static u8 omap4_reg_map[] = { + [OMAP_I2C_REV_REG] = 0x04, + [OMAP_I2C_IE_REG] = 0x2c, + [OMAP_I2C_STAT_REG] = 0x28, + [OMAP_I2C_IV_REG] = 0x34, + [OMAP_I2C_WE_REG] = 0x34, + [OMAP_I2C_SYSS_REG] = 0x90, + [OMAP_I2C_BUF_REG] = 0x94, + [OMAP_I2C_CNT_REG] = 0x98, + [OMAP_I2C_DATA_REG] = 0x9c, + [OMAP_I2C_SYSC_REG] = 0x20, + [OMAP_I2C_CON_REG] = 0xa4, + [OMAP_I2C_OA_REG] = 0xa8, + [OMAP_I2C_SA_REG] = 0xac, + [OMAP_I2C_PSC_REG] = 0xb0, + [OMAP_I2C_SCLL_REG] = 0xb4, + [OMAP_I2C_SCLH_REG] = 0xb8, + [OMAP_I2C_SYSTEST_REG] = 0xbC, + [OMAP_I2C_BUFSTAT_REG] = 0xc0, + [OMAP_I2C_REVNB_LO] = 0x00, + [OMAP_I2C_REVNB_HI] = 0x04, + [OMAP_I2C_IRQSTATUS_RAW] = 0x24, + [OMAP_I2C_IRQENABLE_SET] = 0x2c, + [OMAP_I2C_IRQENABLE_CLR] = 0x30, }; static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, int reg, u16 val) { - __raw_writew(val, i2c_dev->base + (reg << i2c_dev->reg_shift)); + __raw_writew(val, i2c_dev->base + + (i2c_dev->regs[reg] << i2c_dev->reg_shift)); } static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg) { - return __raw_readw(i2c_dev->base + (reg << i2c_dev->reg_shift)); + return __raw_readw(i2c_dev->base + + (i2c_dev->regs[reg] << i2c_dev->reg_shift)); } static int __init omap_i2c_get_clocks(struct omap_i2c_dev *dev) @@ -265,7 +331,11 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev) WARN_ON(dev->idle); dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); - omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0); + if (dev->rev >= OMAP_I2C_REV_ON_4430) + omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_CLR, 1); + else + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0); + if (dev->rev < OMAP_I2C_REV_2) { iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */ } else { @@ -330,7 +400,9 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) * REVISIT: Some wkup sources might not be needed. */ dev->westate = OMAP_I2C_WE_ALL; - omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); + if (dev->rev < OMAP_I2C_REV_ON_4430) + omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, + dev->westate); } } omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); @@ -357,7 +429,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) psc = fclk_rate / 12000000; } - if (cpu_is_omap2430() || cpu_is_omap34xx()) { + if (!(cpu_class_is_omap1() || cpu_is_omap2420())) { /* * HSI2C controller internal clk rate should be 19.2 Mhz for @@ -430,6 +502,11 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) /* Take the I2C module out of reset: */ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + dev->errata = 0; + + if (cpu_is_omap2430() || cpu_is_omap34xx()) + dev->errata |= I2C_OMAP_ERRATA_I207; + /* Enable interrupts */ dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | @@ -539,8 +616,12 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, * REVISIT: We should abort the transfer on signals, but the bus goes * into arbitration and we're currently unable to recover from it. */ + if (dev->set_mpu_wkup_lat != NULL) + dev->set_mpu_wkup_lat(dev->dev, dev->latency); r = wait_for_completion_timeout(&dev->cmd_complete, OMAP_I2C_TIMEOUT); + if (dev->set_mpu_wkup_lat != NULL) + dev->set_mpu_wkup_lat(dev->dev, -1); dev->buf_len = 0; if (r < 0) return r; @@ -623,6 +704,34 @@ omap_i2c_ack_stat(struct omap_i2c_dev *dev, u16 stat) omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); } +static inline void i2c_omap_errata_i207(struct omap_i2c_dev *dev, u16 stat) +{ + /* + * I2C Errata(Errata Nos. OMAP2: 1.67, OMAP3: 1.8) + * Not applicable for OMAP4. + * Under certain rare conditions, RDR could be set again + * when the bus is busy, then ignore the interrupt and + * clear the interrupt. + */ + if (stat & OMAP_I2C_STAT_RDR) { + /* Step 1: If RDR is set, clear it */ + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); + + /* Step 2: */ + if (!(omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) + & OMAP_I2C_STAT_BB)) { + + /* Step 3: */ + if (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) + & OMAP_I2C_STAT_RDR) { + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); + dev_dbg(dev->dev, "RDR when bus is busy.\n"); + } + + } + } +} + /* rev1 devices are apparently only on some 15xx */ #ifdef CONFIG_ARCH_OMAP15XX @@ -684,6 +793,35 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id) #define omap_i2c_rev1_isr NULL #endif +/* + * OMAP3430 Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing + * data to DATA_REG. Otherwise some data bytes can be lost while transferring + * them from the memory to the I2C interface. + */ +static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err) +{ + unsigned long timeout = 10000; + + while (--timeout && !(*stat & OMAP_I2C_STAT_XUDF)) { + if (*stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { + omap_i2c_ack_stat(dev, *stat & (OMAP_I2C_STAT_XRDY | + OMAP_I2C_STAT_XDR)); + *err |= OMAP_I2C_STAT_XUDF; + return -ETIMEDOUT; + } + + cpu_relax(); + *stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); + } + + if (!timeout) { + dev_err(dev->dev, "timeout waiting on XUDF bit\n"); + return 0; + } + + return 0; +} + static irqreturn_t omap_i2c_isr(int this_irq, void *dev_id) { @@ -733,6 +871,10 @@ complete: } if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) { u8 num_bytes = 1; + + if (dev->errata & I2C_OMAP_ERRATA_I207) + i2c_omap_errata_i207(dev, stat); + if (dev->fifo_size) { if (stat & OMAP_I2C_STAT_RRDY) num_bytes = dev->fifo_size; @@ -747,9 +889,12 @@ complete: if (dev->buf_len) { *dev->buf++ = w; dev->buf_len--; - /* Data reg from 2430 is 8 bit wide */ - if (!cpu_is_omap2430() && - !cpu_is_omap34xx()) { + /* + * Data reg in 2430, omap3 and + * omap4 is 8 bit wide + */ + if (cpu_class_is_omap1() || + cpu_is_omap2420()) { if (dev->buf_len) { *dev->buf++ = w >> 8; dev->buf_len--; @@ -787,9 +932,12 @@ complete: if (dev->buf_len) { w = *dev->buf++; dev->buf_len--; - /* Data reg from 2430 is 8 bit wide */ - if (!cpu_is_omap2430() && - !cpu_is_omap34xx()) { + /* + * Data reg in 2430, omap3 and + * omap4 is 8 bit wide + */ + if (cpu_class_is_omap1() || + cpu_is_omap2420()) { if (dev->buf_len) { w |= *dev->buf++ << 8; dev->buf_len--; @@ -807,25 +955,9 @@ complete: break; } - /* - * OMAP3430 Errata 1.153: When an XRDY/XDR - * is hit, wait for XUDF before writing data - * to DATA_REG. Otherwise some data bytes can - * be lost while transferring them from the - * memory to the I2C interface. - */ - - if (dev->rev <= OMAP_I2C_REV_ON_3430) { - while (!(stat & OMAP_I2C_STAT_XUDF)) { - if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { - omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); - err |= OMAP_I2C_STAT_XUDF; - goto complete; - } - cpu_relax(); - stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); - } - } + if ((dev->errata & I2C_OMAP3_1P153) && + errata_omap3_1p153(dev, &stat, &err)) + goto complete; omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); } @@ -857,6 +989,7 @@ omap_i2c_probe(struct platform_device *pdev) struct omap_i2c_dev *dev; struct i2c_adapter *adap; struct resource *mem, *irq, *ioarea; + struct omap_i2c_bus_platform_data *pdata = pdev->dev.platform_data; irq_handler_t isr; int r; u32 speed = 0; @@ -886,10 +1019,13 @@ omap_i2c_probe(struct platform_device *pdev) goto err_release_region; } - if (pdev->dev.platform_data != NULL) - speed = *(u32 *)pdev->dev.platform_data; - else - speed = 100; /* Defualt speed */ + if (pdata != NULL) { + speed = pdata->clkrate; + dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat; + } else { + speed = 100; /* Default speed */ + dev->set_mpu_wkup_lat = NULL; + } dev->speed = speed; dev->idle = 1; @@ -905,17 +1041,27 @@ omap_i2c_probe(struct platform_device *pdev) if (cpu_is_omap7xx()) dev->reg_shift = 1; + else if (cpu_is_omap44xx()) + dev->reg_shift = 0; else dev->reg_shift = 2; if ((r = omap_i2c_get_clocks(dev)) != 0) goto err_iounmap; + if (cpu_is_omap44xx()) + dev->regs = (u8 *) omap4_reg_map; + else + dev->regs = (u8 *) reg_map; + omap_i2c_unidle(dev); dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff; - if (cpu_is_omap2430() || cpu_is_omap34xx()) { + if (dev->rev <= OMAP_I2C_REV_ON_3430) + dev->errata |= I2C_OMAP3_1P153; + + if (!(cpu_class_is_omap1() || cpu_is_omap2420())) { u16 s; /* Set up the fifo size - Get total size */ @@ -927,8 +1073,17 @@ omap_i2c_probe(struct platform_device *pdev) * size. This is to ensure that we can handle the status on int * call back latencies. */ - dev->fifo_size = (dev->fifo_size / 2); - dev->b_hw = 1; /* Enable hardware fixes */ + if (dev->rev >= OMAP_I2C_REV_ON_4430) { + dev->fifo_size = 0; + dev->b_hw = 0; /* Disable hardware fixes */ + } else { + dev->fifo_size = (dev->fifo_size / 2); + dev->b_hw = 1; /* Enable hardware fixes */ + } + /* calculate wakeup latency constraint for MPU */ + if (dev->set_mpu_wkup_lat != NULL) + dev->latency = (1000000 * dev->fifo_size) / + (1000 * speed / 8); } /* reset ASAP, clearing any IRQs */ diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 14d249f5ed3f..fbde6f614059 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -209,18 +209,6 @@ static void i2c_pxa_show_state(struct pxa_i2c *i2c, int lno, const char *fname) } #define show_state(i2c) i2c_pxa_show_state(i2c, __LINE__, __func__) -#else -#define i2c_debug 0 - -#define show_state(i2c) do { } while (0) -#define decode_ISR(val) do { } while (0) -#define decode_ICR(val) do { } while (0) -#endif - -#define eedbg(lvl, x...) do { if ((lvl) < 1) { printk(KERN_DEBUG "" x); } } while(0) - -static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret); -static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id); static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) { @@ -236,6 +224,20 @@ static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) printk("\n"); } +#else /* ifdef DEBUG */ + +#define i2c_debug 0 + +#define show_state(i2c) do { } while (0) +#define decode_ISR(val) do { } while (0) +#define decode_ICR(val) do { } while (0) +#define i2c_pxa_scream_blue_murder(i2c, why) do { } while (0) + +#endif /* ifdef DEBUG / else */ + +static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret); +static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id); + static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c) { return !(readl(_ICR(i2c)) & ICR_SCLE); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index d27072b2249f..ec3256cce91e 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -482,7 +482,8 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) { - unsigned long timeout; + unsigned long iicstat, timeout; + int spins = 20; int ret; if (i2c->suspended) @@ -521,7 +522,21 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, /* ensure the stop has been through the bus */ - msleep(1); + dev_dbg(i2c->dev, "waiting for bus idle\n"); + + /* first, try busy waiting briefly */ + do { + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + } while ((iicstat & S3C2410_IICSTAT_START) && --spins); + + /* if that timed out sleep */ + if (!spins) { + msleep(1); + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + } + + if (iicstat & S3C2410_IICSTAT_START) + dev_warn(i2c->dev, "timeout waiting for bus idle\n"); out: return ret; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index c2258a51fe0c..7c469a62c3c1 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -159,107 +159,131 @@ static void i2c_device_shutdown(struct device *dev) driver->shutdown(client); } -#ifdef CONFIG_SUSPEND -static int i2c_device_pm_suspend(struct device *dev) +#ifdef CONFIG_PM_SLEEP +static int i2c_legacy_suspend(struct device *dev, pm_message_t mesg) { - const struct dev_pm_ops *pm; + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; - if (!dev->driver) + if (!client || !dev->driver) return 0; - pm = dev->driver->pm; - if (!pm || !pm->suspend) + driver = to_i2c_driver(dev->driver); + if (!driver->suspend) return 0; - return pm->suspend(dev); + return driver->suspend(client, mesg); } -static int i2c_device_pm_resume(struct device *dev) +static int i2c_legacy_resume(struct device *dev) { - const struct dev_pm_ops *pm; + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; - if (!dev->driver) + if (!client || !dev->driver) return 0; - pm = dev->driver->pm; - if (!pm || !pm->resume) + driver = to_i2c_driver(dev->driver); + if (!driver->resume) return 0; - return pm->resume(dev); + return driver->resume(client); } -#else -#define i2c_device_pm_suspend NULL -#define i2c_device_pm_resume NULL -#endif -#ifdef CONFIG_PM_RUNTIME -static int i2c_device_runtime_suspend(struct device *dev) +static int i2c_device_pm_suspend(struct device *dev) { - const struct dev_pm_ops *pm; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - if (!dev->driver) - return 0; - pm = dev->driver->pm; - if (!pm || !pm->runtime_suspend) + if (pm_runtime_suspended(dev)) return 0; - return pm->runtime_suspend(dev); -} -static int i2c_device_runtime_resume(struct device *dev) -{ - const struct dev_pm_ops *pm; + if (pm) + return pm->suspend ? pm->suspend(dev) : 0; - if (!dev->driver) - return 0; - pm = dev->driver->pm; - if (!pm || !pm->runtime_resume) - return 0; - return pm->runtime_resume(dev); + return i2c_legacy_suspend(dev, PMSG_SUSPEND); } -static int i2c_device_runtime_idle(struct device *dev) +static int i2c_device_pm_resume(struct device *dev) { - const struct dev_pm_ops *pm = NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int ret; - if (dev->driver) - pm = dev->driver->pm; - if (pm && pm->runtime_idle) { - ret = pm->runtime_idle(dev); - if (ret) - return ret; + if (pm) + ret = pm->resume ? pm->resume(dev) : 0; + else + ret = i2c_legacy_resume(dev); + + if (!ret) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); } - return pm_runtime_suspend(dev); + return ret; } -#else -#define i2c_device_runtime_suspend NULL -#define i2c_device_runtime_resume NULL -#define i2c_device_runtime_idle NULL -#endif -static int i2c_device_suspend(struct device *dev, pm_message_t mesg) +static int i2c_device_pm_freeze(struct device *dev) { - struct i2c_client *client = i2c_verify_client(dev); - struct i2c_driver *driver; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - if (!client || !dev->driver) + if (pm_runtime_suspended(dev)) return 0; - driver = to_i2c_driver(dev->driver); - if (!driver->suspend) - return 0; - return driver->suspend(client, mesg); + + if (pm) + return pm->freeze ? pm->freeze(dev) : 0; + + return i2c_legacy_suspend(dev, PMSG_FREEZE); } -static int i2c_device_resume(struct device *dev) +static int i2c_device_pm_thaw(struct device *dev) { - struct i2c_client *client = i2c_verify_client(dev); - struct i2c_driver *driver; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - if (!client || !dev->driver) + if (pm_runtime_suspended(dev)) return 0; - driver = to_i2c_driver(dev->driver); - if (!driver->resume) + + if (pm) + return pm->thaw ? pm->thaw(dev) : 0; + + return i2c_legacy_resume(dev); +} + +static int i2c_device_pm_poweroff(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm_runtime_suspended(dev)) return 0; - return driver->resume(client); + + if (pm) + return pm->poweroff ? pm->poweroff(dev) : 0; + + return i2c_legacy_suspend(dev, PMSG_HIBERNATE); } +static int i2c_device_pm_restore(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int ret; + + if (pm) + ret = pm->restore ? pm->restore(dev) : 0; + else + ret = i2c_legacy_resume(dev); + + if (!ret) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return ret; +} +#else /* !CONFIG_PM_SLEEP */ +#define i2c_device_pm_suspend NULL +#define i2c_device_pm_resume NULL +#define i2c_device_pm_freeze NULL +#define i2c_device_pm_thaw NULL +#define i2c_device_pm_poweroff NULL +#define i2c_device_pm_restore NULL +#endif /* !CONFIG_PM_SLEEP */ + static void i2c_client_dev_release(struct device *dev) { kfree(to_i2c_client(dev)); @@ -301,9 +325,15 @@ static const struct attribute_group *i2c_dev_attr_groups[] = { static const struct dev_pm_ops i2c_device_pm_ops = { .suspend = i2c_device_pm_suspend, .resume = i2c_device_pm_resume, - .runtime_suspend = i2c_device_runtime_suspend, - .runtime_resume = i2c_device_runtime_resume, - .runtime_idle = i2c_device_runtime_idle, + .freeze = i2c_device_pm_freeze, + .thaw = i2c_device_pm_thaw, + .poweroff = i2c_device_pm_poweroff, + .restore = i2c_device_pm_restore, + SET_RUNTIME_PM_OPS( + pm_generic_runtime_suspend, + pm_generic_runtime_resume, + pm_generic_runtime_idle + ) }; struct bus_type i2c_bus_type = { @@ -312,8 +342,6 @@ struct bus_type i2c_bus_type = { .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, - .suspend = i2c_device_suspend, - .resume = i2c_device_resume, .pm = &i2c_device_pm_ops, }; EXPORT_SYMBOL_GPL(i2c_bus_type); diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c index b85450865ff0..0b7815d2581c 100644 --- a/drivers/ide/ide-cs.c +++ b/drivers/ide/ide-cs.c @@ -65,8 +65,7 @@ MODULE_LICENSE("Dual MPL/GPL"); typedef struct ide_info_t { struct pcmcia_device *p_dev; struct ide_host *host; - int ndev; - dev_node_t node; + int ndev; } ide_info_t; static void ide_release(struct pcmcia_device *); @@ -102,7 +101,6 @@ static int ide_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; link->io.IOAddrLines = 3; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -285,8 +283,7 @@ static int ide_config(struct pcmcia_device *link) io_base = link->io.BasePort1; ctl_base = stk->ctl_base; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) + if (!link->irq) goto failed; ret = pcmcia_request_configuration(link, &link->conf); if (ret) @@ -299,24 +296,21 @@ static int ide_config(struct pcmcia_device *link) if (is_kme) outb(0x81, ctl_base+1); - host = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); + host = idecs_register(io_base, ctl_base, link->irq, link); if (host == NULL && link->io.NumPorts1 == 0x20) { outb(0x02, ctl_base + 0x10); host = idecs_register(io_base + 0x10, ctl_base + 0x10, - link->irq.AssignedIRQ, link); + link->irq, link); } if (host == NULL) goto failed; info->ndev = 1; - sprintf(info->node.dev_name, "hd%c", 'a' + host->ports[0]->index * 2); - info->node.major = host->ports[0]->major; - info->node.minor = 0; info->host = host; - link->dev_node = &info->node; - printk(KERN_INFO "ide-cs: %s: Vpp = %d.%d\n", - info->node.dev_name, link->conf.Vpp / 10, link->conf.Vpp % 10); + dev_info(&link->dev, "ide-cs: hd%c: Vpp = %d.%d\n", + 'a' + host->ports[0]->index * 2, + link->conf.Vpp / 10, link->conf.Vpp % 10); kfree(stk); return 0; diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 975adce5f40c..330d2a423362 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -46,6 +46,7 @@ source "drivers/infiniband/hw/ipath/Kconfig" source "drivers/infiniband/hw/ehca/Kconfig" source "drivers/infiniband/hw/amso1100/Kconfig" source "drivers/infiniband/hw/cxgb3/Kconfig" +source "drivers/infiniband/hw/cxgb4/Kconfig" source "drivers/infiniband/hw/mlx4/Kconfig" source "drivers/infiniband/hw/nes/Kconfig" diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile index ed35e4496241..0c4e589d746e 100644 --- a/drivers/infiniband/Makefile +++ b/drivers/infiniband/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INFINIBAND_IPATH) += hw/ipath/ obj-$(CONFIG_INFINIBAND_EHCA) += hw/ehca/ obj-$(CONFIG_INFINIBAND_AMSO1100) += hw/amso1100/ obj-$(CONFIG_INFINIBAND_CXGB3) += hw/cxgb3/ +obj-$(CONFIG_INFINIBAND_CXGB4) += hw/cxgb4/ obj-$(CONFIG_MLX4_INFINIBAND) += hw/mlx4/ obj-$(CONFIG_INFINIBAND_NES) += hw/nes/ obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/ diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 6d777069d86d..b930b8110a63 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -79,7 +79,6 @@ static DEFINE_IDR(sdp_ps); static DEFINE_IDR(tcp_ps); static DEFINE_IDR(udp_ps); static DEFINE_IDR(ipoib_ps); -static int next_port; struct cma_device { struct list_head list; @@ -1677,13 +1676,13 @@ int rdma_set_ib_paths(struct rdma_cm_id *id, if (!cma_comp_exch(id_priv, CMA_ADDR_RESOLVED, CMA_ROUTE_RESOLVED)) return -EINVAL; - id->route.path_rec = kmalloc(sizeof *path_rec * num_paths, GFP_KERNEL); + id->route.path_rec = kmemdup(path_rec, sizeof *path_rec * num_paths, + GFP_KERNEL); if (!id->route.path_rec) { ret = -ENOMEM; goto err; } - memcpy(id->route.path_rec, path_rec, sizeof *path_rec * num_paths); id->route.num_paths = num_paths; return 0; err: @@ -1970,47 +1969,33 @@ err1: static int cma_alloc_any_port(struct idr *ps, struct rdma_id_private *id_priv) { - struct rdma_bind_list *bind_list; - int port, ret, low, high; - - bind_list = kzalloc(sizeof *bind_list, GFP_KERNEL); - if (!bind_list) - return -ENOMEM; - -retry: - /* FIXME: add proper port randomization per like inet_csk_get_port */ - do { - ret = idr_get_new_above(ps, bind_list, next_port, &port); - } while ((ret == -EAGAIN) && idr_pre_get(ps, GFP_KERNEL)); - - if (ret) - goto err1; + static unsigned int last_used_port; + int low, high, remaining; + unsigned int rover; inet_get_local_port_range(&low, &high); - if (port > high) { - if (next_port != low) { - idr_remove(ps, port); - next_port = low; - goto retry; - } - ret = -EADDRNOTAVAIL; - goto err2; + remaining = (high - low) + 1; + rover = net_random() % remaining + low; +retry: + if (last_used_port != rover && + !idr_find(ps, (unsigned short) rover)) { + int ret = cma_alloc_port(ps, id_priv, rover); + /* + * Remember previously used port number in order to avoid + * re-using same port immediately after it is closed. + */ + if (!ret) + last_used_port = rover; + if (ret != -EADDRNOTAVAIL) + return ret; } - - if (port == high) - next_port = low; - else - next_port = port + 1; - - bind_list->ps = ps; - bind_list->port = (unsigned short) port; - cma_bind_port(bind_list, id_priv); - return 0; -err2: - idr_remove(ps, port); -err1: - kfree(bind_list); - return ret; + if (--remaining) { + rover++; + if ((rover < low) || (rover > high)) + rover = low; + goto retry; + } + return -EADDRNOTAVAIL; } static int cma_use_port(struct idr *ps, struct rdma_id_private *id_priv) @@ -2995,12 +2980,7 @@ static void cma_remove_one(struct ib_device *device) static int __init cma_init(void) { - int ret, low, high, remaining; - - get_random_bytes(&next_port, sizeof next_port); - inet_get_local_port_range(&low, &high); - remaining = (high - low) + 1; - next_port = ((unsigned int) next_port % remaining) + low; + int ret; cma_wq = create_singlethread_workqueue("rdma_cm"); if (!cma_wq) diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 1df1194aeba4..6dc7b77d5d29 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -291,13 +291,11 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device, } if (mad_reg_req) { - reg_req = kmalloc(sizeof *reg_req, GFP_KERNEL); + reg_req = kmemdup(mad_reg_req, sizeof *reg_req, GFP_KERNEL); if (!reg_req) { ret = ERR_PTR(-ENOMEM); goto error3; } - /* Make a copy of the MAD registration request */ - memcpy(reg_req, mad_reg_req, sizeof *reg_req); } /* Now, fill in the various structures */ diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index 512b1c43460c..46474842cfe9 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -1181,7 +1181,7 @@ static int ib_ucm_open(struct inode *inode, struct file *filp) file->filp = filp; file->device = container_of(inode->i_cdev, struct ib_ucm_device, cdev); - return 0; + return nonseekable_open(inode, filp); } static int ib_ucm_close(struct inode *inode, struct file *filp) @@ -1229,6 +1229,7 @@ static const struct file_operations ucm_fops = { .release = ib_ucm_close, .write = ib_ucm_write, .poll = ib_ucm_poll, + .llseek = no_llseek, }; static ssize_t show_ibdev(struct device *dev, struct device_attribute *attr, diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 46185084121e..ac7edc24165c 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1220,7 +1220,8 @@ static int ucma_open(struct inode *inode, struct file *filp) filp->private_data = file; file->filp = filp; - return 0; + + return nonseekable_open(inode, filp); } static int ucma_close(struct inode *inode, struct file *filp) @@ -1250,6 +1251,7 @@ static const struct file_operations ucma_fops = { .release = ucma_close, .write = ucma_write, .poll = ucma_poll, + .llseek = no_llseek, }; static struct miscdevice ucma_misc = { diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index e7db054fb1c8..6babb72b39fc 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -781,7 +781,7 @@ static int ib_umad_open(struct inode *inode, struct file *filp) { struct ib_umad_port *port; struct ib_umad_file *file; - int ret = 0; + int ret; port = container_of(inode->i_cdev, struct ib_umad_port, cdev); if (port) @@ -814,6 +814,8 @@ static int ib_umad_open(struct inode *inode, struct file *filp) list_add_tail(&file->port_list, &port->file_list); + ret = nonseekable_open(inode, filp); + out: mutex_unlock(&port->file_mutex); return ret; @@ -866,7 +868,8 @@ static const struct file_operations umad_fops = { .compat_ioctl = ib_umad_compat_ioctl, #endif .open = ib_umad_open, - .release = ib_umad_close + .release = ib_umad_close, + .llseek = no_llseek, }; static int ib_umad_sm_open(struct inode *inode, struct file *filp) @@ -903,7 +906,7 @@ static int ib_umad_sm_open(struct inode *inode, struct file *filp) filp->private_data = port; - return 0; + return nonseekable_open(inode, filp); fail: kref_put(&port->umad_dev->ref, ib_umad_release_dev); @@ -933,7 +936,8 @@ static int ib_umad_sm_close(struct inode *inode, struct file *filp) static const struct file_operations umad_sm_fops = { .owner = THIS_MODULE, .open = ib_umad_sm_open, - .release = ib_umad_sm_close + .release = ib_umad_sm_close, + .llseek = no_llseek, }; static struct ib_client umad_client = { diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index fb3526254426..ec83e9fe387b 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -369,7 +369,8 @@ static const struct file_operations uverbs_event_fops = { .read = ib_uverbs_event_read, .poll = ib_uverbs_event_poll, .release = ib_uverbs_event_close, - .fasync = ib_uverbs_event_fasync + .fasync = ib_uverbs_event_fasync, + .llseek = no_llseek, }; void ib_uverbs_comp_handler(struct ib_cq *cq, void *cq_context) @@ -623,7 +624,7 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp) filp->private_data = file; - return 0; + return nonseekable_open(inode, filp); err_module: module_put(dev->ib_dev->owner); @@ -651,7 +652,8 @@ static const struct file_operations uverbs_fops = { .owner = THIS_MODULE, .write = ib_uverbs_write, .open = ib_uverbs_open, - .release = ib_uverbs_close + .release = ib_uverbs_close, + .llseek = no_llseek, }; static const struct file_operations uverbs_mmap_fops = { @@ -659,7 +661,8 @@ static const struct file_operations uverbs_mmap_fops = { .write = ib_uverbs_write, .mmap = ib_uverbs_mmap, .open = ib_uverbs_open, - .release = ib_uverbs_close + .release = ib_uverbs_close, + .llseek = no_llseek, }; static struct ib_client uverbs_client = { diff --git a/drivers/infiniband/hw/amso1100/c2.h b/drivers/infiniband/hw/amso1100/c2.h index f7ff66f98361..6ae698e68775 100644 --- a/drivers/infiniband/hw/amso1100/c2.h +++ b/drivers/infiniband/hw/amso1100/c2.h @@ -250,7 +250,7 @@ struct c2_array { struct sp_chunk { struct sp_chunk *next; dma_addr_t dma_addr; - DECLARE_PCI_UNMAP_ADDR(mapping); + DEFINE_DMA_UNMAP_ADDR(mapping); u16 head; u16 shared_ptr[0]; }; diff --git a/drivers/infiniband/hw/amso1100/c2_alloc.c b/drivers/infiniband/hw/amso1100/c2_alloc.c index d4f5f5d42e90..78d247ec6961 100644 --- a/drivers/infiniband/hw/amso1100/c2_alloc.c +++ b/drivers/infiniband/hw/amso1100/c2_alloc.c @@ -49,7 +49,7 @@ static int c2_alloc_mqsp_chunk(struct c2_dev *c2dev, gfp_t gfp_mask, return -ENOMEM; new_head->dma_addr = dma_addr; - pci_unmap_addr_set(new_head, mapping, new_head->dma_addr); + dma_unmap_addr_set(new_head, mapping, new_head->dma_addr); new_head->next = NULL; new_head->head = 0; @@ -81,7 +81,7 @@ void c2_free_mqsp_pool(struct c2_dev *c2dev, struct sp_chunk *root) while (root) { next = root->next; dma_free_coherent(&c2dev->pcidev->dev, PAGE_SIZE, root, - pci_unmap_addr(root, mapping)); + dma_unmap_addr(root, mapping)); root = next; } } diff --git a/drivers/infiniband/hw/amso1100/c2_cq.c b/drivers/infiniband/hw/amso1100/c2_cq.c index f7b0fc23f413..49e0e8533f74 100644 --- a/drivers/infiniband/hw/amso1100/c2_cq.c +++ b/drivers/infiniband/hw/amso1100/c2_cq.c @@ -257,7 +257,7 @@ int c2_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_flags) static void c2_free_cq_buf(struct c2_dev *c2dev, struct c2_mq *mq) { dma_free_coherent(&c2dev->pcidev->dev, mq->q_size * mq->msg_size, - mq->msg_pool.host, pci_unmap_addr(mq, mapping)); + mq->msg_pool.host, dma_unmap_addr(mq, mapping)); } static int c2_alloc_cq_buf(struct c2_dev *c2dev, struct c2_mq *mq, int q_size, @@ -278,7 +278,7 @@ static int c2_alloc_cq_buf(struct c2_dev *c2dev, struct c2_mq *mq, int q_size, NULL, /* peer (currently unknown) */ C2_MQ_HOST_TARGET); - pci_unmap_addr_set(mq, mapping, mq->host_dma); + dma_unmap_addr_set(mq, mapping, mq->host_dma); return 0; } diff --git a/drivers/infiniband/hw/amso1100/c2_mq.h b/drivers/infiniband/hw/amso1100/c2_mq.h index acede007b94a..fc1b9a7cec4b 100644 --- a/drivers/infiniband/hw/amso1100/c2_mq.h +++ b/drivers/infiniband/hw/amso1100/c2_mq.h @@ -71,7 +71,7 @@ struct c2_mq { u8 __iomem *adapter; } msg_pool; dma_addr_t host_dma; - DECLARE_PCI_UNMAP_ADDR(mapping); + DEFINE_DMA_UNMAP_ADDR(mapping); u16 hint_count; u16 priv; struct c2_mq_shared __iomem *peer; diff --git a/drivers/infiniband/hw/amso1100/c2_provider.h b/drivers/infiniband/hw/amso1100/c2_provider.h index 1076df2ee96a..bf189987711f 100644 --- a/drivers/infiniband/hw/amso1100/c2_provider.h +++ b/drivers/infiniband/hw/amso1100/c2_provider.h @@ -50,7 +50,7 @@ struct c2_buf_list { void *buf; - DECLARE_PCI_UNMAP_ADDR(mapping) + DEFINE_DMA_UNMAP_ADDR(mapping); }; diff --git a/drivers/infiniband/hw/amso1100/c2_rnic.c b/drivers/infiniband/hw/amso1100/c2_rnic.c index 78c4bcc6ef60..85cfae4cad71 100644 --- a/drivers/infiniband/hw/amso1100/c2_rnic.c +++ b/drivers/infiniband/hw/amso1100/c2_rnic.c @@ -524,7 +524,7 @@ int __devinit c2_rnic_init(struct c2_dev *c2dev) err = -ENOMEM; goto bail1; } - pci_unmap_addr_set(&c2dev->rep_vq, mapping, c2dev->rep_vq.host_dma); + dma_unmap_addr_set(&c2dev->rep_vq, mapping, c2dev->rep_vq.host_dma); pr_debug("%s rep_vq va %p dma %llx\n", __func__, q1_pages, (unsigned long long) c2dev->rep_vq.host_dma); c2_mq_rep_init(&c2dev->rep_vq, @@ -545,7 +545,7 @@ int __devinit c2_rnic_init(struct c2_dev *c2dev) err = -ENOMEM; goto bail2; } - pci_unmap_addr_set(&c2dev->aeq, mapping, c2dev->aeq.host_dma); + dma_unmap_addr_set(&c2dev->aeq, mapping, c2dev->aeq.host_dma); pr_debug("%s aeq va %p dma %llx\n", __func__, q2_pages, (unsigned long long) c2dev->aeq.host_dma); c2_mq_rep_init(&c2dev->aeq, @@ -596,11 +596,11 @@ int __devinit c2_rnic_init(struct c2_dev *c2dev) bail3: dma_free_coherent(&c2dev->pcidev->dev, c2dev->aeq.q_size * c2dev->aeq.msg_size, - q2_pages, pci_unmap_addr(&c2dev->aeq, mapping)); + q2_pages, dma_unmap_addr(&c2dev->aeq, mapping)); bail2: dma_free_coherent(&c2dev->pcidev->dev, c2dev->rep_vq.q_size * c2dev->rep_vq.msg_size, - q1_pages, pci_unmap_addr(&c2dev->rep_vq, mapping)); + q1_pages, dma_unmap_addr(&c2dev->rep_vq, mapping)); bail1: c2_free_mqsp_pool(c2dev, c2dev->kern_mqsp_pool); bail0: @@ -637,13 +637,13 @@ void __devexit c2_rnic_term(struct c2_dev *c2dev) dma_free_coherent(&c2dev->pcidev->dev, c2dev->aeq.q_size * c2dev->aeq.msg_size, c2dev->aeq.msg_pool.host, - pci_unmap_addr(&c2dev->aeq, mapping)); + dma_unmap_addr(&c2dev->aeq, mapping)); /* Free the verbs reply queue */ dma_free_coherent(&c2dev->pcidev->dev, c2dev->rep_vq.q_size * c2dev->rep_vq.msg_size, c2dev->rep_vq.msg_pool.host, - pci_unmap_addr(&c2dev->rep_vq, mapping)); + dma_unmap_addr(&c2dev->rep_vq, mapping)); /* Free the MQ shared pointer pool */ c2_free_mqsp_pool(c2dev, c2dev->kern_mqsp_pool); diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c index 35f286f1ad1e..005b7b52bc1e 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.c +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c @@ -174,7 +174,7 @@ int cxio_create_cq(struct cxio_rdev *rdev_p, struct t3_cq *cq, int kernel) kfree(cq->sw_queue); return -ENOMEM; } - pci_unmap_addr_set(cq, mapping, cq->dma_addr); + dma_unmap_addr_set(cq, mapping, cq->dma_addr); memset(cq->queue, 0, size); setup.id = cq->cqid; setup.base_addr = (u64) (cq->dma_addr); @@ -297,7 +297,7 @@ int cxio_create_qp(struct cxio_rdev *rdev_p, u32 kernel_domain, goto err4; memset(wq->queue, 0, depth * sizeof(union t3_wr)); - pci_unmap_addr_set(wq, mapping, wq->dma_addr); + dma_unmap_addr_set(wq, mapping, wq->dma_addr); wq->doorbell = (void __iomem *)rdev_p->rnic_info.kdb_addr; if (!kernel_domain) wq->udb = (u64)rdev_p->rnic_info.udbell_physbase + @@ -325,7 +325,7 @@ int cxio_destroy_cq(struct cxio_rdev *rdev_p, struct t3_cq *cq) dma_free_coherent(&(rdev_p->rnic_info.pdev->dev), (1UL << (cq->size_log2)) * sizeof(struct t3_cqe), cq->queue, - pci_unmap_addr(cq, mapping)); + dma_unmap_addr(cq, mapping)); cxio_hal_put_cqid(rdev_p->rscp, cq->cqid); return err; } @@ -336,7 +336,7 @@ int cxio_destroy_qp(struct cxio_rdev *rdev_p, struct t3_wq *wq, dma_free_coherent(&(rdev_p->rnic_info.pdev->dev), (1UL << (wq->size_log2)) * sizeof(union t3_wr), wq->queue, - pci_unmap_addr(wq, mapping)); + dma_unmap_addr(wq, mapping)); kfree(wq->sq); cxio_hal_rqtpool_free(rdev_p, wq->rq_addr, (1UL << wq->rq_size_log2)); kfree(wq->rq); @@ -537,7 +537,7 @@ static int cxio_hal_init_ctrl_qp(struct cxio_rdev *rdev_p) err = -ENOMEM; goto err; } - pci_unmap_addr_set(&rdev_p->ctrl_qp, mapping, + dma_unmap_addr_set(&rdev_p->ctrl_qp, mapping, rdev_p->ctrl_qp.dma_addr); rdev_p->ctrl_qp.doorbell = (void __iomem *)rdev_p->rnic_info.kdb_addr; memset(rdev_p->ctrl_qp.workq, 0, @@ -583,7 +583,7 @@ static int cxio_hal_destroy_ctrl_qp(struct cxio_rdev *rdev_p) dma_free_coherent(&(rdev_p->rnic_info.pdev->dev), (1UL << T3_CTRL_QP_SIZE_LOG2) * sizeof(union t3_wr), rdev_p->ctrl_qp.workq, - pci_unmap_addr(&rdev_p->ctrl_qp, mapping)); + dma_unmap_addr(&rdev_p->ctrl_qp, mapping)); return cxio_hal_clear_qp_ctx(rdev_p, T3_CTRL_QP_ID); } diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.h b/drivers/infiniband/hw/cxgb3/cxio_hal.h index 073373c2c560..8f0caf7d4482 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.h +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.h @@ -71,7 +71,7 @@ struct cxio_hal_ctrl_qp { wait_queue_head_t waitq;/* wait for RspQ/CQE msg */ union t3_wr *workq; /* the work request queue */ dma_addr_t dma_addr; /* pci bus address of the workq */ - DECLARE_PCI_UNMAP_ADDR(mapping) + DEFINE_DMA_UNMAP_ADDR(mapping); void __iomem *doorbell; }; diff --git a/drivers/infiniband/hw/cxgb3/cxio_wr.h b/drivers/infiniband/hw/cxgb3/cxio_wr.h index 15073b2da1c5..e5ddb63e7d23 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_wr.h +++ b/drivers/infiniband/hw/cxgb3/cxio_wr.h @@ -691,7 +691,7 @@ struct t3_swrq { struct t3_wq { union t3_wr *queue; /* DMA accessable memory */ dma_addr_t dma_addr; /* DMA address for HW */ - DECLARE_PCI_UNMAP_ADDR(mapping) /* unmap kruft */ + DEFINE_DMA_UNMAP_ADDR(mapping); /* unmap kruft */ u32 error; /* 1 once we go to ERROR */ u32 qpid; u32 wptr; /* idx to next available WR slot */ @@ -718,7 +718,7 @@ struct t3_cq { u32 wptr; u32 size_log2; dma_addr_t dma_addr; - DECLARE_PCI_UNMAP_ADDR(mapping) + DEFINE_DMA_UNMAP_ADDR(mapping); struct t3_cqe *queue; struct t3_cqe *sw_queue; u32 sw_rptr; diff --git a/drivers/infiniband/hw/cxgb3/iwch.c b/drivers/infiniband/hw/cxgb3/iwch.c index 63f975f3e30f..8e77dc543dd1 100644 --- a/drivers/infiniband/hw/cxgb3/iwch.c +++ b/drivers/infiniband/hw/cxgb3/iwch.c @@ -47,8 +47,6 @@ MODULE_DESCRIPTION("Chelsio T3 RDMA Driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(DRV_VERSION); -cxgb3_cpl_handler_func t3c_handlers[NUM_CPL_CMDS]; - static void open_rnic_dev(struct t3cdev *); static void close_rnic_dev(struct t3cdev *); static void iwch_event_handler(struct t3cdev *, u32, u32); diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index 4fef03296276..ebfb117ba68b 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -102,12 +102,9 @@ static unsigned int cong_flavor = 1; module_param(cong_flavor, uint, 0644); MODULE_PARM_DESC(cong_flavor, "TCP Congestion control flavor (default=1)"); -static void process_work(struct work_struct *work); static struct workqueue_struct *workq; -static DECLARE_WORK(skb_work, process_work); static struct sk_buff_head rxq; -static cxgb3_cpl_handler_func work_handlers[NUM_CPL_CMDS]; static struct sk_buff *get_skb(struct sk_buff *skb, int len, gfp_t gfp); static void ep_timeout(unsigned long arg); @@ -151,7 +148,7 @@ int iwch_l2t_send(struct t3cdev *tdev, struct sk_buff *skb, struct l2t_entry *l2 return -EIO; } error = l2t_send(tdev, skb, l2e); - if (error) + if (error < 0) kfree_skb(skb); return error; } @@ -167,7 +164,7 @@ int iwch_cxgb3_ofld_send(struct t3cdev *tdev, struct sk_buff *skb) return -EIO; } error = cxgb3_ofld_send(tdev, skb); - if (error) + if (error < 0) kfree_skb(skb); return error; } @@ -302,27 +299,6 @@ static void release_ep_resources(struct iwch_ep *ep) put_ep(&ep->com); } -static void process_work(struct work_struct *work) -{ - struct sk_buff *skb = NULL; - void *ep; - struct t3cdev *tdev; - int ret; - - while ((skb = skb_dequeue(&rxq))) { - ep = *((void **) (skb->cb)); - tdev = *((struct t3cdev **) (skb->cb + sizeof(void *))); - ret = work_handlers[G_OPCODE(ntohl((__force __be32)skb->csum))](tdev, skb, ep); - if (ret & CPL_RET_BUF_DONE) - kfree_skb(skb); - - /* - * ep was referenced in sched(), and is freed here. - */ - put_ep((struct iwch_ep_common *)ep); - } -} - static int status2errno(int status) { switch (status) { @@ -2157,7 +2133,49 @@ int iwch_ep_redirect(void *ctx, struct dst_entry *old, struct dst_entry *new, /* * All the CM events are handled on a work queue to have a safe context. + * These are the real handlers that are called from the work queue. */ +static const cxgb3_cpl_handler_func work_handlers[NUM_CPL_CMDS] = { + [CPL_ACT_ESTABLISH] = act_establish, + [CPL_ACT_OPEN_RPL] = act_open_rpl, + [CPL_RX_DATA] = rx_data, + [CPL_TX_DMA_ACK] = tx_ack, + [CPL_ABORT_RPL_RSS] = abort_rpl, + [CPL_ABORT_RPL] = abort_rpl, + [CPL_PASS_OPEN_RPL] = pass_open_rpl, + [CPL_CLOSE_LISTSRV_RPL] = close_listsrv_rpl, + [CPL_PASS_ACCEPT_REQ] = pass_accept_req, + [CPL_PASS_ESTABLISH] = pass_establish, + [CPL_PEER_CLOSE] = peer_close, + [CPL_ABORT_REQ_RSS] = peer_abort, + [CPL_CLOSE_CON_RPL] = close_con_rpl, + [CPL_RDMA_TERMINATE] = terminate, + [CPL_RDMA_EC_STATUS] = ec_status, +}; + +static void process_work(struct work_struct *work) +{ + struct sk_buff *skb = NULL; + void *ep; + struct t3cdev *tdev; + int ret; + + while ((skb = skb_dequeue(&rxq))) { + ep = *((void **) (skb->cb)); + tdev = *((struct t3cdev **) (skb->cb + sizeof(void *))); + ret = work_handlers[G_OPCODE(ntohl((__force __be32)skb->csum))](tdev, skb, ep); + if (ret & CPL_RET_BUF_DONE) + kfree_skb(skb); + + /* + * ep was referenced in sched(), and is freed here. + */ + put_ep((struct iwch_ep_common *)ep); + } +} + +static DECLARE_WORK(skb_work, process_work); + static int sched(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) { struct iwch_ep_common *epc = ctx; @@ -2189,6 +2207,29 @@ static int set_tcb_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) return CPL_RET_BUF_DONE; } +/* + * All upcalls from the T3 Core go to sched() to schedule the + * processing on a work queue. + */ +cxgb3_cpl_handler_func t3c_handlers[NUM_CPL_CMDS] = { + [CPL_ACT_ESTABLISH] = sched, + [CPL_ACT_OPEN_RPL] = sched, + [CPL_RX_DATA] = sched, + [CPL_TX_DMA_ACK] = sched, + [CPL_ABORT_RPL_RSS] = sched, + [CPL_ABORT_RPL] = sched, + [CPL_PASS_OPEN_RPL] = sched, + [CPL_CLOSE_LISTSRV_RPL] = sched, + [CPL_PASS_ACCEPT_REQ] = sched, + [CPL_PASS_ESTABLISH] = sched, + [CPL_PEER_CLOSE] = sched, + [CPL_CLOSE_CON_RPL] = sched, + [CPL_ABORT_REQ_RSS] = sched, + [CPL_RDMA_TERMINATE] = sched, + [CPL_RDMA_EC_STATUS] = sched, + [CPL_SET_TCB_RPL] = set_tcb_rpl, +}; + int __init iwch_cm_init(void) { skb_queue_head_init(&rxq); @@ -2197,46 +2238,6 @@ int __init iwch_cm_init(void) if (!workq) return -ENOMEM; - /* - * All upcalls from the T3 Core go to sched() to - * schedule the processing on a work queue. - */ - t3c_handlers[CPL_ACT_ESTABLISH] = sched; - t3c_handlers[CPL_ACT_OPEN_RPL] = sched; - t3c_handlers[CPL_RX_DATA] = sched; - t3c_handlers[CPL_TX_DMA_ACK] = sched; - t3c_handlers[CPL_ABORT_RPL_RSS] = sched; - t3c_handlers[CPL_ABORT_RPL] = sched; - t3c_handlers[CPL_PASS_OPEN_RPL] = sched; - t3c_handlers[CPL_CLOSE_LISTSRV_RPL] = sched; - t3c_handlers[CPL_PASS_ACCEPT_REQ] = sched; - t3c_handlers[CPL_PASS_ESTABLISH] = sched; - t3c_handlers[CPL_PEER_CLOSE] = sched; - t3c_handlers[CPL_CLOSE_CON_RPL] = sched; - t3c_handlers[CPL_ABORT_REQ_RSS] = sched; - t3c_handlers[CPL_RDMA_TERMINATE] = sched; - t3c_handlers[CPL_RDMA_EC_STATUS] = sched; - t3c_handlers[CPL_SET_TCB_RPL] = set_tcb_rpl; - - /* - * These are the real handlers that are called from a - * work queue. - */ - work_handlers[CPL_ACT_ESTABLISH] = act_establish; - work_handlers[CPL_ACT_OPEN_RPL] = act_open_rpl; - work_handlers[CPL_RX_DATA] = rx_data; - work_handlers[CPL_TX_DMA_ACK] = tx_ack; - work_handlers[CPL_ABORT_RPL_RSS] = abort_rpl; - work_handlers[CPL_ABORT_RPL] = abort_rpl; - work_handlers[CPL_PASS_OPEN_RPL] = pass_open_rpl; - work_handlers[CPL_CLOSE_LISTSRV_RPL] = close_listsrv_rpl; - work_handlers[CPL_PASS_ACCEPT_REQ] = pass_accept_req; - work_handlers[CPL_PASS_ESTABLISH] = pass_establish; - work_handlers[CPL_PEER_CLOSE] = peer_close; - work_handlers[CPL_ABORT_REQ_RSS] = peer_abort; - work_handlers[CPL_CLOSE_CON_RPL] = close_con_rpl; - work_handlers[CPL_RDMA_TERMINATE] = terminate; - work_handlers[CPL_RDMA_EC_STATUS] = ec_status; return 0; } diff --git a/drivers/infiniband/hw/cxgb4/Kconfig b/drivers/infiniband/hw/cxgb4/Kconfig new file mode 100644 index 000000000000..ccb85eaaad75 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/Kconfig @@ -0,0 +1,18 @@ +config INFINIBAND_CXGB4 + tristate "Chelsio T4 RDMA Driver" + depends on CHELSIO_T4 && INET + select GENERIC_ALLOCATOR + ---help--- + This is an iWARP/RDMA driver for the Chelsio T4 1GbE and + 10GbE adapters. + + For general information about Chelsio and our products, visit + our website at <http://www.chelsio.com>. + + For customer support, please visit our customer support page at + <http://www.chelsio.com/support.htm>. + + Please send feedback to <linux-bugs@chelsio.com>. + + To compile this driver as a module, choose M here: the module + will be called iw_cxgb4. diff --git a/drivers/infiniband/hw/cxgb4/Makefile b/drivers/infiniband/hw/cxgb4/Makefile new file mode 100644 index 000000000000..e31a499f0172 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/Makefile @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Idrivers/net/cxgb4 + +obj-$(CONFIG_INFINIBAND_CXGB4) += iw_cxgb4.o + +iw_cxgb4-y := device.o cm.o provider.o mem.o cq.o qp.o resource.o ev.o diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c new file mode 100644 index 000000000000..30ce0a8eca09 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -0,0 +1,2374 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + */ +#include <linux/module.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/skbuff.h> +#include <linux/timer.h> +#include <linux/notifier.h> +#include <linux/inetdevice.h> +#include <linux/ip.h> +#include <linux/tcp.h> + +#include <net/neighbour.h> +#include <net/netevent.h> +#include <net/route.h> + +#include "iw_cxgb4.h" + +static char *states[] = { + "idle", + "listen", + "connecting", + "mpa_wait_req", + "mpa_req_sent", + "mpa_req_rcvd", + "mpa_rep_sent", + "fpdu_mode", + "aborting", + "closing", + "moribund", + "dead", + NULL, +}; + +int c4iw_max_read_depth = 8; +module_param(c4iw_max_read_depth, int, 0644); +MODULE_PARM_DESC(c4iw_max_read_depth, "Per-connection max ORD/IRD (default=8)"); + +static int enable_tcp_timestamps; +module_param(enable_tcp_timestamps, int, 0644); +MODULE_PARM_DESC(enable_tcp_timestamps, "Enable tcp timestamps (default=0)"); + +static int enable_tcp_sack; +module_param(enable_tcp_sack, int, 0644); +MODULE_PARM_DESC(enable_tcp_sack, "Enable tcp SACK (default=0)"); + +static int enable_tcp_window_scaling = 1; +module_param(enable_tcp_window_scaling, int, 0644); +MODULE_PARM_DESC(enable_tcp_window_scaling, + "Enable tcp window scaling (default=1)"); + +int c4iw_debug; +module_param(c4iw_debug, int, 0644); +MODULE_PARM_DESC(c4iw_debug, "Enable debug logging (default=0)"); + +static int peer2peer; +module_param(peer2peer, int, 0644); +MODULE_PARM_DESC(peer2peer, "Support peer2peer ULPs (default=0)"); + +static int p2p_type = FW_RI_INIT_P2PTYPE_READ_REQ; +module_param(p2p_type, int, 0644); +MODULE_PARM_DESC(p2p_type, "RDMAP opcode to use for the RTR message: " + "1=RDMA_READ 0=RDMA_WRITE (default 1)"); + +static int ep_timeout_secs = 60; +module_param(ep_timeout_secs, int, 0644); +MODULE_PARM_DESC(ep_timeout_secs, "CM Endpoint operation timeout " + "in seconds (default=60)"); + +static int mpa_rev = 1; +module_param(mpa_rev, int, 0644); +MODULE_PARM_DESC(mpa_rev, "MPA Revision, 0 supports amso1100, " + "1 is spec compliant. (default=1)"); + +static int markers_enabled; +module_param(markers_enabled, int, 0644); +MODULE_PARM_DESC(markers_enabled, "Enable MPA MARKERS (default(0)=disabled)"); + +static int crc_enabled = 1; +module_param(crc_enabled, int, 0644); +MODULE_PARM_DESC(crc_enabled, "Enable MPA CRC (default(1)=enabled)"); + +static int rcv_win = 256 * 1024; +module_param(rcv_win, int, 0644); +MODULE_PARM_DESC(rcv_win, "TCP receive window in bytes (default=256KB)"); + +static int snd_win = 32 * 1024; +module_param(snd_win, int, 0644); +MODULE_PARM_DESC(snd_win, "TCP send window in bytes (default=32KB)"); + +static struct workqueue_struct *workq; + +static struct sk_buff_head rxq; + +static struct sk_buff *get_skb(struct sk_buff *skb, int len, gfp_t gfp); +static void ep_timeout(unsigned long arg); +static void connect_reply_upcall(struct c4iw_ep *ep, int status); + +static LIST_HEAD(timeout_list); +static spinlock_t timeout_lock; + +static void start_ep_timer(struct c4iw_ep *ep) +{ + PDBG("%s ep %p\n", __func__, ep); + if (timer_pending(&ep->timer)) { + PDBG("%s stopped / restarted timer ep %p\n", __func__, ep); + del_timer_sync(&ep->timer); + } else + c4iw_get_ep(&ep->com); + ep->timer.expires = jiffies + ep_timeout_secs * HZ; + ep->timer.data = (unsigned long)ep; + ep->timer.function = ep_timeout; + add_timer(&ep->timer); +} + +static void stop_ep_timer(struct c4iw_ep *ep) +{ + PDBG("%s ep %p\n", __func__, ep); + if (!timer_pending(&ep->timer)) { + printk(KERN_ERR "%s timer stopped when its not running! " + "ep %p state %u\n", __func__, ep, ep->com.state); + WARN_ON(1); + return; + } + del_timer_sync(&ep->timer); + c4iw_put_ep(&ep->com); +} + +static int c4iw_l2t_send(struct c4iw_rdev *rdev, struct sk_buff *skb, + struct l2t_entry *l2e) +{ + int error = 0; + + if (c4iw_fatal_error(rdev)) { + kfree_skb(skb); + PDBG("%s - device in error state - dropping\n", __func__); + return -EIO; + } + error = cxgb4_l2t_send(rdev->lldi.ports[0], skb, l2e); + if (error < 0) + kfree_skb(skb); + return error; +} + +int c4iw_ofld_send(struct c4iw_rdev *rdev, struct sk_buff *skb) +{ + int error = 0; + + if (c4iw_fatal_error(rdev)) { + kfree_skb(skb); + PDBG("%s - device in error state - dropping\n", __func__); + return -EIO; + } + error = cxgb4_ofld_send(rdev->lldi.ports[0], skb); + if (error < 0) + kfree_skb(skb); + return error; +} + +static void release_tid(struct c4iw_rdev *rdev, u32 hwtid, struct sk_buff *skb) +{ + struct cpl_tid_release *req; + + skb = get_skb(skb, sizeof *req, GFP_KERNEL); + if (!skb) + return; + req = (struct cpl_tid_release *) skb_put(skb, sizeof(*req)); + INIT_TP_WR(req, hwtid); + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_TID_RELEASE, hwtid)); + set_wr_txq(skb, CPL_PRIORITY_SETUP, 0); + c4iw_ofld_send(rdev, skb); + return; +} + +static void set_emss(struct c4iw_ep *ep, u16 opt) +{ + ep->emss = ep->com.dev->rdev.lldi.mtus[GET_TCPOPT_MSS(opt)] - 40; + ep->mss = ep->emss; + if (GET_TCPOPT_TSTAMP(opt)) + ep->emss -= 12; + if (ep->emss < 128) + ep->emss = 128; + PDBG("%s mss_idx %u mss %u emss=%u\n", __func__, GET_TCPOPT_MSS(opt), + ep->mss, ep->emss); +} + +static enum c4iw_ep_state state_read(struct c4iw_ep_common *epc) +{ + unsigned long flags; + enum c4iw_ep_state state; + + spin_lock_irqsave(&epc->lock, flags); + state = epc->state; + spin_unlock_irqrestore(&epc->lock, flags); + return state; +} + +static void __state_set(struct c4iw_ep_common *epc, enum c4iw_ep_state new) +{ + epc->state = new; +} + +static void state_set(struct c4iw_ep_common *epc, enum c4iw_ep_state new) +{ + unsigned long flags; + + spin_lock_irqsave(&epc->lock, flags); + PDBG("%s - %s -> %s\n", __func__, states[epc->state], states[new]); + __state_set(epc, new); + spin_unlock_irqrestore(&epc->lock, flags); + return; +} + +static void *alloc_ep(int size, gfp_t gfp) +{ + struct c4iw_ep_common *epc; + + epc = kzalloc(size, gfp); + if (epc) { + kref_init(&epc->kref); + spin_lock_init(&epc->lock); + init_waitqueue_head(&epc->waitq); + } + PDBG("%s alloc ep %p\n", __func__, epc); + return epc; +} + +void _c4iw_free_ep(struct kref *kref) +{ + struct c4iw_ep *ep; + + ep = container_of(kref, struct c4iw_ep, com.kref); + PDBG("%s ep %p state %s\n", __func__, ep, states[state_read(&ep->com)]); + if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) { + cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid); + dst_release(ep->dst); + cxgb4_l2t_release(ep->l2t); + } + kfree(ep); +} + +static void release_ep_resources(struct c4iw_ep *ep) +{ + set_bit(RELEASE_RESOURCES, &ep->com.flags); + c4iw_put_ep(&ep->com); +} + +static int status2errno(int status) +{ + switch (status) { + case CPL_ERR_NONE: + return 0; + case CPL_ERR_CONN_RESET: + return -ECONNRESET; + case CPL_ERR_ARP_MISS: + return -EHOSTUNREACH; + case CPL_ERR_CONN_TIMEDOUT: + return -ETIMEDOUT; + case CPL_ERR_TCAM_FULL: + return -ENOMEM; + case CPL_ERR_CONN_EXIST: + return -EADDRINUSE; + default: + return -EIO; + } +} + +/* + * Try and reuse skbs already allocated... + */ +static struct sk_buff *get_skb(struct sk_buff *skb, int len, gfp_t gfp) +{ + if (skb && !skb_is_nonlinear(skb) && !skb_cloned(skb)) { + skb_trim(skb, 0); + skb_get(skb); + skb_reset_transport_header(skb); + } else { + skb = alloc_skb(len, gfp); + } + return skb; +} + +static struct rtable *find_route(struct c4iw_dev *dev, __be32 local_ip, + __be32 peer_ip, __be16 local_port, + __be16 peer_port, u8 tos) +{ + struct rtable *rt; + struct flowi fl = { + .oif = 0, + .nl_u = { + .ip4_u = { + .daddr = peer_ip, + .saddr = local_ip, + .tos = tos} + }, + .proto = IPPROTO_TCP, + .uli_u = { + .ports = { + .sport = local_port, + .dport = peer_port} + } + }; + + if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0)) + return NULL; + return rt; +} + +static void arp_failure_discard(void *handle, struct sk_buff *skb) +{ + PDBG("%s c4iw_dev %p\n", __func__, handle); + kfree_skb(skb); +} + +/* + * Handle an ARP failure for an active open. + */ +static void act_open_req_arp_failure(void *handle, struct sk_buff *skb) +{ + printk(KERN_ERR MOD "ARP failure duing connect\n"); + kfree_skb(skb); +} + +/* + * Handle an ARP failure for a CPL_ABORT_REQ. Change it into a no RST variant + * and send it along. + */ +static void abort_arp_failure(void *handle, struct sk_buff *skb) +{ + struct c4iw_rdev *rdev = handle; + struct cpl_abort_req *req = cplhdr(skb); + + PDBG("%s rdev %p\n", __func__, rdev); + req->cmd = CPL_ABORT_NO_RST; + c4iw_ofld_send(rdev, skb); +} + +static void send_flowc(struct c4iw_ep *ep, struct sk_buff *skb) +{ + unsigned int flowclen = 80; + struct fw_flowc_wr *flowc; + int i; + + skb = get_skb(skb, flowclen, GFP_KERNEL); + flowc = (struct fw_flowc_wr *)__skb_put(skb, flowclen); + + flowc->op_to_nparams = cpu_to_be32(FW_WR_OP(FW_FLOWC_WR) | + FW_FLOWC_WR_NPARAMS(8)); + flowc->flowid_len16 = cpu_to_be32(FW_WR_LEN16(DIV_ROUND_UP(flowclen, + 16)) | FW_WR_FLOWID(ep->hwtid)); + + flowc->mnemval[0].mnemonic = FW_FLOWC_MNEM_PFNVFN; + flowc->mnemval[0].val = cpu_to_be32(0); + flowc->mnemval[1].mnemonic = FW_FLOWC_MNEM_CH; + flowc->mnemval[1].val = cpu_to_be32(ep->tx_chan); + flowc->mnemval[2].mnemonic = FW_FLOWC_MNEM_PORT; + flowc->mnemval[2].val = cpu_to_be32(ep->tx_chan); + flowc->mnemval[3].mnemonic = FW_FLOWC_MNEM_IQID; + flowc->mnemval[3].val = cpu_to_be32(ep->rss_qid); + flowc->mnemval[4].mnemonic = FW_FLOWC_MNEM_SNDNXT; + flowc->mnemval[4].val = cpu_to_be32(ep->snd_seq); + flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_RCVNXT; + flowc->mnemval[5].val = cpu_to_be32(ep->rcv_seq); + flowc->mnemval[6].mnemonic = FW_FLOWC_MNEM_SNDBUF; + flowc->mnemval[6].val = cpu_to_be32(snd_win); + flowc->mnemval[7].mnemonic = FW_FLOWC_MNEM_MSS; + flowc->mnemval[7].val = cpu_to_be32(ep->emss); + /* Pad WR to 16 byte boundary */ + flowc->mnemval[8].mnemonic = 0; + flowc->mnemval[8].val = 0; + for (i = 0; i < 9; i++) { + flowc->mnemval[i].r4[0] = 0; + flowc->mnemval[i].r4[1] = 0; + flowc->mnemval[i].r4[2] = 0; + } + + set_wr_txq(skb, CPL_PRIORITY_DATA, ep->txq_idx); + c4iw_ofld_send(&ep->com.dev->rdev, skb); +} + +static int send_halfclose(struct c4iw_ep *ep, gfp_t gfp) +{ + struct cpl_close_con_req *req; + struct sk_buff *skb; + int wrlen = roundup(sizeof *req, 16); + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + skb = get_skb(NULL, wrlen, gfp); + if (!skb) { + printk(KERN_ERR MOD "%s - failed to alloc skb\n", __func__); + return -ENOMEM; + } + set_wr_txq(skb, CPL_PRIORITY_DATA, ep->txq_idx); + t4_set_arp_err_handler(skb, NULL, arp_failure_discard); + req = (struct cpl_close_con_req *) skb_put(skb, wrlen); + memset(req, 0, wrlen); + INIT_TP_WR(req, ep->hwtid); + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_CLOSE_CON_REQ, + ep->hwtid)); + return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); +} + +static int send_abort(struct c4iw_ep *ep, struct sk_buff *skb, gfp_t gfp) +{ + struct cpl_abort_req *req; + int wrlen = roundup(sizeof *req, 16); + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + skb = get_skb(skb, wrlen, gfp); + if (!skb) { + printk(KERN_ERR MOD "%s - failed to alloc skb.\n", + __func__); + return -ENOMEM; + } + set_wr_txq(skb, CPL_PRIORITY_DATA, ep->txq_idx); + t4_set_arp_err_handler(skb, &ep->com.dev->rdev, abort_arp_failure); + req = (struct cpl_abort_req *) skb_put(skb, wrlen); + memset(req, 0, wrlen); + INIT_TP_WR(req, ep->hwtid); + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ABORT_REQ, ep->hwtid)); + req->cmd = CPL_ABORT_SEND_RST; + return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); +} + +static int send_connect(struct c4iw_ep *ep) +{ + struct cpl_act_open_req *req; + struct sk_buff *skb; + u64 opt0; + u32 opt2; + unsigned int mtu_idx; + int wscale; + int wrlen = roundup(sizeof *req, 16); + + PDBG("%s ep %p atid %u\n", __func__, ep, ep->atid); + + skb = get_skb(NULL, wrlen, GFP_KERNEL); + if (!skb) { + printk(KERN_ERR MOD "%s - failed to alloc skb.\n", + __func__); + return -ENOMEM; + } + set_wr_txq(skb, CPL_PRIORITY_SETUP, ep->txq_idx); + + cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx); + wscale = compute_wscale(rcv_win); + opt0 = KEEP_ALIVE(1) | + WND_SCALE(wscale) | + MSS_IDX(mtu_idx) | + L2T_IDX(ep->l2t->idx) | + TX_CHAN(ep->tx_chan) | + SMAC_SEL(ep->smac_idx) | + DSCP(ep->tos) | + RCV_BUFSIZ(rcv_win>>10); + opt2 = RX_CHANNEL(0) | + RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid); + if (enable_tcp_timestamps) + opt2 |= TSTAMPS_EN(1); + if (enable_tcp_sack) + opt2 |= SACK_EN(1); + if (wscale && enable_tcp_window_scaling) + opt2 |= WND_SCALE_EN(1); + t4_set_arp_err_handler(skb, NULL, act_open_req_arp_failure); + + req = (struct cpl_act_open_req *) skb_put(skb, wrlen); + INIT_TP_WR(req, 0); + OPCODE_TID(req) = cpu_to_be32( + MK_OPCODE_TID(CPL_ACT_OPEN_REQ, ((ep->rss_qid<<14)|ep->atid))); + req->local_port = ep->com.local_addr.sin_port; + req->peer_port = ep->com.remote_addr.sin_port; + req->local_ip = ep->com.local_addr.sin_addr.s_addr; + req->peer_ip = ep->com.remote_addr.sin_addr.s_addr; + req->opt0 = cpu_to_be64(opt0); + req->params = 0; + req->opt2 = cpu_to_be32(opt2); + return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); +} + +static void send_mpa_req(struct c4iw_ep *ep, struct sk_buff *skb) +{ + int mpalen, wrlen; + struct fw_ofld_tx_data_wr *req; + struct mpa_message *mpa; + + PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen); + + BUG_ON(skb_cloned(skb)); + + mpalen = sizeof(*mpa) + ep->plen; + wrlen = roundup(mpalen + sizeof *req, 16); + skb = get_skb(skb, wrlen, GFP_KERNEL); + if (!skb) { + connect_reply_upcall(ep, -ENOMEM); + return; + } + set_wr_txq(skb, CPL_PRIORITY_DATA, ep->txq_idx); + + req = (struct fw_ofld_tx_data_wr *)skb_put(skb, wrlen); + memset(req, 0, wrlen); + req->op_to_immdlen = cpu_to_be32( + FW_WR_OP(FW_OFLD_TX_DATA_WR) | + FW_WR_COMPL(1) | + FW_WR_IMMDLEN(mpalen)); + req->flowid_len16 = cpu_to_be32( + FW_WR_FLOWID(ep->hwtid) | + FW_WR_LEN16(wrlen >> 4)); + req->plen = cpu_to_be32(mpalen); + req->tunnel_to_proxy = cpu_to_be32( + FW_OFLD_TX_DATA_WR_FLUSH(1) | + FW_OFLD_TX_DATA_WR_SHOVE(1)); + + mpa = (struct mpa_message *)(req + 1); + memcpy(mpa->key, MPA_KEY_REQ, sizeof(mpa->key)); + mpa->flags = (crc_enabled ? MPA_CRC : 0) | + (markers_enabled ? MPA_MARKERS : 0); + mpa->private_data_size = htons(ep->plen); + mpa->revision = mpa_rev; + + if (ep->plen) + memcpy(mpa->private_data, ep->mpa_pkt + sizeof(*mpa), ep->plen); + + /* + * Reference the mpa skb. This ensures the data area + * will remain in memory until the hw acks the tx. + * Function fw4_ack() will deref it. + */ + skb_get(skb); + t4_set_arp_err_handler(skb, NULL, arp_failure_discard); + BUG_ON(ep->mpa_skb); + ep->mpa_skb = skb; + c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); + start_ep_timer(ep); + state_set(&ep->com, MPA_REQ_SENT); + ep->mpa_attr.initiator = 1; + return; +} + +static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen) +{ + int mpalen, wrlen; + struct fw_ofld_tx_data_wr *req; + struct mpa_message *mpa; + struct sk_buff *skb; + + PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen); + + mpalen = sizeof(*mpa) + plen; + wrlen = roundup(mpalen + sizeof *req, 16); + + skb = get_skb(NULL, wrlen, GFP_KERNEL); + if (!skb) { + printk(KERN_ERR MOD "%s - cannot alloc skb!\n", __func__); + return -ENOMEM; + } + set_wr_txq(skb, CPL_PRIORITY_DATA, ep->txq_idx); + + req = (struct fw_ofld_tx_data_wr *)skb_put(skb, wrlen); + memset(req, 0, wrlen); + req->op_to_immdlen = cpu_to_be32( + FW_WR_OP(FW_OFLD_TX_DATA_WR) | + FW_WR_COMPL(1) | + FW_WR_IMMDLEN(mpalen)); + req->flowid_len16 = cpu_to_be32( + FW_WR_FLOWID(ep->hwtid) | + FW_WR_LEN16(wrlen >> 4)); + req->plen = cpu_to_be32(mpalen); + req->tunnel_to_proxy = cpu_to_be32( + FW_OFLD_TX_DATA_WR_FLUSH(1) | + FW_OFLD_TX_DATA_WR_SHOVE(1)); + + mpa = (struct mpa_message *)(req + 1); + memset(mpa, 0, sizeof(*mpa)); + memcpy(mpa->key, MPA_KEY_REP, sizeof(mpa->key)); + mpa->flags = MPA_REJECT; + mpa->revision = mpa_rev; + mpa->private_data_size = htons(plen); + if (plen) + memcpy(mpa->private_data, pdata, plen); + + /* + * Reference the mpa skb again. This ensures the data area + * will remain in memory until the hw acks the tx. + * Function fw4_ack() will deref it. + */ + skb_get(skb); + set_wr_txq(skb, CPL_PRIORITY_DATA, ep->txq_idx); + t4_set_arp_err_handler(skb, NULL, arp_failure_discard); + BUG_ON(ep->mpa_skb); + ep->mpa_skb = skb; + return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); +} + +static int send_mpa_reply(struct c4iw_ep *ep, const void *pdata, u8 plen) +{ + int mpalen, wrlen; + struct fw_ofld_tx_data_wr *req; + struct mpa_message *mpa; + struct sk_buff *skb; + + PDBG("%s ep %p tid %u pd_len %d\n", __func__, ep, ep->hwtid, ep->plen); + + mpalen = sizeof(*mpa) + plen; + wrlen = roundup(mpalen + sizeof *req, 16); + + skb = get_skb(NULL, wrlen, GFP_KERNEL); + if (!skb) { + printk(KERN_ERR MOD "%s - cannot alloc skb!\n", __func__); + return -ENOMEM; + } + set_wr_txq(skb, CPL_PRIORITY_DATA, ep->txq_idx); + + req = (struct fw_ofld_tx_data_wr *) skb_put(skb, wrlen); + memset(req, 0, wrlen); + req->op_to_immdlen = cpu_to_be32( + FW_WR_OP(FW_OFLD_TX_DATA_WR) | + FW_WR_COMPL(1) | + FW_WR_IMMDLEN(mpalen)); + req->flowid_len16 = cpu_to_be32( + FW_WR_FLOWID(ep->hwtid) | + FW_WR_LEN16(wrlen >> 4)); + req->plen = cpu_to_be32(mpalen); + req->tunnel_to_proxy = cpu_to_be32( + FW_OFLD_TX_DATA_WR_FLUSH(1) | + FW_OFLD_TX_DATA_WR_SHOVE(1)); + + mpa = (struct mpa_message *)(req + 1); + memset(mpa, 0, sizeof(*mpa)); + memcpy(mpa->key, MPA_KEY_REP, sizeof(mpa->key)); + mpa->flags = (ep->mpa_attr.crc_enabled ? MPA_CRC : 0) | + (markers_enabled ? MPA_MARKERS : 0); + mpa->revision = mpa_rev; + mpa->private_data_size = htons(plen); + if (plen) + memcpy(mpa->private_data, pdata, plen); + + /* + * Reference the mpa skb. This ensures the data area + * will remain in memory until the hw acks the tx. + * Function fw4_ack() will deref it. + */ + skb_get(skb); + t4_set_arp_err_handler(skb, NULL, arp_failure_discard); + ep->mpa_skb = skb; + state_set(&ep->com, MPA_REP_SENT); + return c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); +} + +static int act_establish(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *ep; + struct cpl_act_establish *req = cplhdr(skb); + unsigned int tid = GET_TID(req); + unsigned int atid = GET_TID_TID(ntohl(req->tos_atid)); + struct tid_info *t = dev->rdev.lldi.tids; + + ep = lookup_atid(t, atid); + + PDBG("%s ep %p tid %u snd_isn %u rcv_isn %u\n", __func__, ep, tid, + be32_to_cpu(req->snd_isn), be32_to_cpu(req->rcv_isn)); + + dst_confirm(ep->dst); + + /* setup the hwtid for this connection */ + ep->hwtid = tid; + cxgb4_insert_tid(t, ep, tid); + + ep->snd_seq = be32_to_cpu(req->snd_isn); + ep->rcv_seq = be32_to_cpu(req->rcv_isn); + + set_emss(ep, ntohs(req->tcp_opt)); + + /* dealloc the atid */ + cxgb4_free_atid(t, atid); + + /* start MPA negotiation */ + send_flowc(ep, NULL); + send_mpa_req(ep, skb); + + return 0; +} + +static void close_complete_upcall(struct c4iw_ep *ep) +{ + struct iw_cm_event event; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + memset(&event, 0, sizeof(event)); + event.event = IW_CM_EVENT_CLOSE; + if (ep->com.cm_id) { + PDBG("close complete delivered ep %p cm_id %p tid %u\n", + ep, ep->com.cm_id, ep->hwtid); + ep->com.cm_id->event_handler(ep->com.cm_id, &event); + ep->com.cm_id->rem_ref(ep->com.cm_id); + ep->com.cm_id = NULL; + ep->com.qp = NULL; + } +} + +static int abort_connection(struct c4iw_ep *ep, struct sk_buff *skb, gfp_t gfp) +{ + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + close_complete_upcall(ep); + state_set(&ep->com, ABORTING); + return send_abort(ep, skb, gfp); +} + +static void peer_close_upcall(struct c4iw_ep *ep) +{ + struct iw_cm_event event; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + memset(&event, 0, sizeof(event)); + event.event = IW_CM_EVENT_DISCONNECT; + if (ep->com.cm_id) { + PDBG("peer close delivered ep %p cm_id %p tid %u\n", + ep, ep->com.cm_id, ep->hwtid); + ep->com.cm_id->event_handler(ep->com.cm_id, &event); + } +} + +static void peer_abort_upcall(struct c4iw_ep *ep) +{ + struct iw_cm_event event; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + memset(&event, 0, sizeof(event)); + event.event = IW_CM_EVENT_CLOSE; + event.status = -ECONNRESET; + if (ep->com.cm_id) { + PDBG("abort delivered ep %p cm_id %p tid %u\n", ep, + ep->com.cm_id, ep->hwtid); + ep->com.cm_id->event_handler(ep->com.cm_id, &event); + ep->com.cm_id->rem_ref(ep->com.cm_id); + ep->com.cm_id = NULL; + ep->com.qp = NULL; + } +} + +static void connect_reply_upcall(struct c4iw_ep *ep, int status) +{ + struct iw_cm_event event; + + PDBG("%s ep %p tid %u status %d\n", __func__, ep, ep->hwtid, status); + memset(&event, 0, sizeof(event)); + event.event = IW_CM_EVENT_CONNECT_REPLY; + event.status = status; + event.local_addr = ep->com.local_addr; + event.remote_addr = ep->com.remote_addr; + + if ((status == 0) || (status == -ECONNREFUSED)) { + event.private_data_len = ep->plen; + event.private_data = ep->mpa_pkt + sizeof(struct mpa_message); + } + if (ep->com.cm_id) { + PDBG("%s ep %p tid %u status %d\n", __func__, ep, + ep->hwtid, status); + ep->com.cm_id->event_handler(ep->com.cm_id, &event); + } + if (status < 0) { + ep->com.cm_id->rem_ref(ep->com.cm_id); + ep->com.cm_id = NULL; + ep->com.qp = NULL; + } +} + +static void connect_request_upcall(struct c4iw_ep *ep) +{ + struct iw_cm_event event; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + memset(&event, 0, sizeof(event)); + event.event = IW_CM_EVENT_CONNECT_REQUEST; + event.local_addr = ep->com.local_addr; + event.remote_addr = ep->com.remote_addr; + event.private_data_len = ep->plen; + event.private_data = ep->mpa_pkt + sizeof(struct mpa_message); + event.provider_data = ep; + if (state_read(&ep->parent_ep->com) != DEAD) { + c4iw_get_ep(&ep->com); + ep->parent_ep->com.cm_id->event_handler( + ep->parent_ep->com.cm_id, + &event); + } + c4iw_put_ep(&ep->parent_ep->com); + ep->parent_ep = NULL; +} + +static void established_upcall(struct c4iw_ep *ep) +{ + struct iw_cm_event event; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + memset(&event, 0, sizeof(event)); + event.event = IW_CM_EVENT_ESTABLISHED; + if (ep->com.cm_id) { + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + ep->com.cm_id->event_handler(ep->com.cm_id, &event); + } +} + +static int update_rx_credits(struct c4iw_ep *ep, u32 credits) +{ + struct cpl_rx_data_ack *req; + struct sk_buff *skb; + int wrlen = roundup(sizeof *req, 16); + + PDBG("%s ep %p tid %u credits %u\n", __func__, ep, ep->hwtid, credits); + skb = get_skb(NULL, wrlen, GFP_KERNEL); + if (!skb) { + printk(KERN_ERR MOD "update_rx_credits - cannot alloc skb!\n"); + return 0; + } + + req = (struct cpl_rx_data_ack *) skb_put(skb, wrlen); + memset(req, 0, wrlen); + INIT_TP_WR(req, ep->hwtid); + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_RX_DATA_ACK, + ep->hwtid)); + req->credit_dack = cpu_to_be32(credits); + set_wr_txq(skb, CPL_PRIORITY_ACK, ep->txq_idx); + c4iw_ofld_send(&ep->com.dev->rdev, skb); + return credits; +} + +static void process_mpa_reply(struct c4iw_ep *ep, struct sk_buff *skb) +{ + struct mpa_message *mpa; + u16 plen; + struct c4iw_qp_attributes attrs; + enum c4iw_qp_attr_mask mask; + int err; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + + /* + * Stop mpa timer. If it expired, then the state has + * changed and we bail since ep_timeout already aborted + * the connection. + */ + stop_ep_timer(ep); + if (state_read(&ep->com) != MPA_REQ_SENT) + return; + + /* + * If we get more than the supported amount of private data + * then we must fail this connection. + */ + if (ep->mpa_pkt_len + skb->len > sizeof(ep->mpa_pkt)) { + err = -EINVAL; + goto err; + } + + /* + * copy the new data into our accumulation buffer. + */ + skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]), + skb->len); + ep->mpa_pkt_len += skb->len; + + /* + * if we don't even have the mpa message, then bail. + */ + if (ep->mpa_pkt_len < sizeof(*mpa)) + return; + mpa = (struct mpa_message *) ep->mpa_pkt; + + /* Validate MPA header. */ + if (mpa->revision != mpa_rev) { + err = -EPROTO; + goto err; + } + if (memcmp(mpa->key, MPA_KEY_REP, sizeof(mpa->key))) { + err = -EPROTO; + goto err; + } + + plen = ntohs(mpa->private_data_size); + + /* + * Fail if there's too much private data. + */ + if (plen > MPA_MAX_PRIVATE_DATA) { + err = -EPROTO; + goto err; + } + + /* + * If plen does not account for pkt size + */ + if (ep->mpa_pkt_len > (sizeof(*mpa) + plen)) { + err = -EPROTO; + goto err; + } + + ep->plen = (u8) plen; + + /* + * If we don't have all the pdata yet, then bail. + * We'll continue process when more data arrives. + */ + if (ep->mpa_pkt_len < (sizeof(*mpa) + plen)) + return; + + if (mpa->flags & MPA_REJECT) { + err = -ECONNREFUSED; + goto err; + } + + /* + * If we get here we have accumulated the entire mpa + * start reply message including private data. And + * the MPA header is valid. + */ + state_set(&ep->com, FPDU_MODE); + ep->mpa_attr.crc_enabled = (mpa->flags & MPA_CRC) | crc_enabled ? 1 : 0; + ep->mpa_attr.recv_marker_enabled = markers_enabled; + ep->mpa_attr.xmit_marker_enabled = mpa->flags & MPA_MARKERS ? 1 : 0; + ep->mpa_attr.version = mpa_rev; + ep->mpa_attr.p2p_type = peer2peer ? p2p_type : + FW_RI_INIT_P2PTYPE_DISABLED; + PDBG("%s - crc_enabled=%d, recv_marker_enabled=%d, " + "xmit_marker_enabled=%d, version=%d\n", __func__, + ep->mpa_attr.crc_enabled, ep->mpa_attr.recv_marker_enabled, + ep->mpa_attr.xmit_marker_enabled, ep->mpa_attr.version); + + attrs.mpa_attr = ep->mpa_attr; + attrs.max_ird = ep->ird; + attrs.max_ord = ep->ord; + attrs.llp_stream_handle = ep; + attrs.next_state = C4IW_QP_STATE_RTS; + + mask = C4IW_QP_ATTR_NEXT_STATE | + C4IW_QP_ATTR_LLP_STREAM_HANDLE | C4IW_QP_ATTR_MPA_ATTR | + C4IW_QP_ATTR_MAX_IRD | C4IW_QP_ATTR_MAX_ORD; + + /* bind QP and TID with INIT_WR */ + err = c4iw_modify_qp(ep->com.qp->rhp, + ep->com.qp, mask, &attrs, 1); + if (err) + goto err; + goto out; +err: + abort_connection(ep, skb, GFP_KERNEL); +out: + connect_reply_upcall(ep, err); + return; +} + +static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb) +{ + struct mpa_message *mpa; + u16 plen; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + + if (state_read(&ep->com) != MPA_REQ_WAIT) + return; + + /* + * If we get more than the supported amount of private data + * then we must fail this connection. + */ + if (ep->mpa_pkt_len + skb->len > sizeof(ep->mpa_pkt)) { + stop_ep_timer(ep); + abort_connection(ep, skb, GFP_KERNEL); + return; + } + + PDBG("%s enter (%s line %u)\n", __func__, __FILE__, __LINE__); + + /* + * Copy the new data into our accumulation buffer. + */ + skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]), + skb->len); + ep->mpa_pkt_len += skb->len; + + /* + * If we don't even have the mpa message, then bail. + * We'll continue process when more data arrives. + */ + if (ep->mpa_pkt_len < sizeof(*mpa)) + return; + + PDBG("%s enter (%s line %u)\n", __func__, __FILE__, __LINE__); + stop_ep_timer(ep); + mpa = (struct mpa_message *) ep->mpa_pkt; + + /* + * Validate MPA Header. + */ + if (mpa->revision != mpa_rev) { + abort_connection(ep, skb, GFP_KERNEL); + return; + } + + if (memcmp(mpa->key, MPA_KEY_REQ, sizeof(mpa->key))) { + abort_connection(ep, skb, GFP_KERNEL); + return; + } + + plen = ntohs(mpa->private_data_size); + + /* + * Fail if there's too much private data. + */ + if (plen > MPA_MAX_PRIVATE_DATA) { + abort_connection(ep, skb, GFP_KERNEL); + return; + } + + /* + * If plen does not account for pkt size + */ + if (ep->mpa_pkt_len > (sizeof(*mpa) + plen)) { + abort_connection(ep, skb, GFP_KERNEL); + return; + } + ep->plen = (u8) plen; + + /* + * If we don't have all the pdata yet, then bail. + */ + if (ep->mpa_pkt_len < (sizeof(*mpa) + plen)) + return; + + /* + * If we get here we have accumulated the entire mpa + * start reply message including private data. + */ + ep->mpa_attr.initiator = 0; + ep->mpa_attr.crc_enabled = (mpa->flags & MPA_CRC) | crc_enabled ? 1 : 0; + ep->mpa_attr.recv_marker_enabled = markers_enabled; + ep->mpa_attr.xmit_marker_enabled = mpa->flags & MPA_MARKERS ? 1 : 0; + ep->mpa_attr.version = mpa_rev; + ep->mpa_attr.p2p_type = peer2peer ? p2p_type : + FW_RI_INIT_P2PTYPE_DISABLED; + PDBG("%s - crc_enabled=%d, recv_marker_enabled=%d, " + "xmit_marker_enabled=%d, version=%d p2p_type=%d\n", __func__, + ep->mpa_attr.crc_enabled, ep->mpa_attr.recv_marker_enabled, + ep->mpa_attr.xmit_marker_enabled, ep->mpa_attr.version, + ep->mpa_attr.p2p_type); + + state_set(&ep->com, MPA_REQ_RCVD); + + /* drive upcall */ + connect_request_upcall(ep); + return; +} + +static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *ep; + struct cpl_rx_data *hdr = cplhdr(skb); + unsigned int dlen = ntohs(hdr->len); + unsigned int tid = GET_TID(hdr); + struct tid_info *t = dev->rdev.lldi.tids; + + ep = lookup_tid(t, tid); + PDBG("%s ep %p tid %u dlen %u\n", __func__, ep, ep->hwtid, dlen); + skb_pull(skb, sizeof(*hdr)); + skb_trim(skb, dlen); + + ep->rcv_seq += dlen; + BUG_ON(ep->rcv_seq != (ntohl(hdr->seq) + dlen)); + + /* update RX credits */ + update_rx_credits(ep, dlen); + + switch (state_read(&ep->com)) { + case MPA_REQ_SENT: + process_mpa_reply(ep, skb); + break; + case MPA_REQ_WAIT: + process_mpa_request(ep, skb); + break; + case MPA_REP_SENT: + break; + default: + printk(KERN_ERR MOD "%s Unexpected streaming data." + " ep %p state %d tid %u\n", + __func__, ep, state_read(&ep->com), ep->hwtid); + + /* + * The ep will timeout and inform the ULP of the failure. + * See ep_timeout(). + */ + break; + } + return 0; +} + +static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *ep; + struct cpl_abort_rpl_rss *rpl = cplhdr(skb); + unsigned long flags; + int release = 0; + unsigned int tid = GET_TID(rpl); + struct tid_info *t = dev->rdev.lldi.tids; + + ep = lookup_tid(t, tid); + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + BUG_ON(!ep); + spin_lock_irqsave(&ep->com.lock, flags); + switch (ep->com.state) { + case ABORTING: + __state_set(&ep->com, DEAD); + release = 1; + break; + default: + printk(KERN_ERR "%s ep %p state %d\n", + __func__, ep, ep->com.state); + break; + } + spin_unlock_irqrestore(&ep->com.lock, flags); + + if (release) + release_ep_resources(ep); + return 0; +} + +/* + * Return whether a failed active open has allocated a TID + */ +static inline int act_open_has_tid(int status) +{ + return status != CPL_ERR_TCAM_FULL && status != CPL_ERR_CONN_EXIST && + status != CPL_ERR_ARP_MISS; +} + +static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *ep; + struct cpl_act_open_rpl *rpl = cplhdr(skb); + unsigned int atid = GET_TID_TID(GET_AOPEN_ATID( + ntohl(rpl->atid_status))); + struct tid_info *t = dev->rdev.lldi.tids; + int status = GET_AOPEN_STATUS(ntohl(rpl->atid_status)); + + ep = lookup_atid(t, atid); + + PDBG("%s ep %p atid %u status %u errno %d\n", __func__, ep, atid, + status, status2errno(status)); + + if (status == CPL_ERR_RTX_NEG_ADVICE) { + printk(KERN_WARNING MOD "Connection problems for atid %u\n", + atid); + return 0; + } + + connect_reply_upcall(ep, status2errno(status)); + state_set(&ep->com, DEAD); + + if (status && act_open_has_tid(status)) + cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, GET_TID(rpl)); + + cxgb4_free_atid(t, atid); + dst_release(ep->dst); + cxgb4_l2t_release(ep->l2t); + c4iw_put_ep(&ep->com); + + return 0; +} + +static int pass_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct cpl_pass_open_rpl *rpl = cplhdr(skb); + struct tid_info *t = dev->rdev.lldi.tids; + unsigned int stid = GET_TID(rpl); + struct c4iw_listen_ep *ep = lookup_stid(t, stid); + + if (!ep) { + printk(KERN_ERR MOD "stid %d lookup failure!\n", stid); + return 0; + } + PDBG("%s ep %p status %d error %d\n", __func__, ep, + rpl->status, status2errno(rpl->status)); + ep->com.rpl_err = status2errno(rpl->status); + ep->com.rpl_done = 1; + wake_up(&ep->com.waitq); + + return 0; +} + +static int listen_stop(struct c4iw_listen_ep *ep) +{ + struct sk_buff *skb; + struct cpl_close_listsvr_req *req; + + PDBG("%s ep %p\n", __func__, ep); + skb = get_skb(NULL, sizeof(*req), GFP_KERNEL); + if (!skb) { + printk(KERN_ERR MOD "%s - failed to alloc skb\n", __func__); + return -ENOMEM; + } + req = (struct cpl_close_listsvr_req *) skb_put(skb, sizeof(*req)); + INIT_TP_WR(req, 0); + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ, + ep->stid)); + req->reply_ctrl = cpu_to_be16( + QUEUENO(ep->com.dev->rdev.lldi.rxq_ids[0])); + set_wr_txq(skb, CPL_PRIORITY_SETUP, 0); + return c4iw_ofld_send(&ep->com.dev->rdev, skb); +} + +static int close_listsrv_rpl(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct cpl_close_listsvr_rpl *rpl = cplhdr(skb); + struct tid_info *t = dev->rdev.lldi.tids; + unsigned int stid = GET_TID(rpl); + struct c4iw_listen_ep *ep = lookup_stid(t, stid); + + PDBG("%s ep %p\n", __func__, ep); + ep->com.rpl_err = status2errno(rpl->status); + ep->com.rpl_done = 1; + wake_up(&ep->com.waitq); + return 0; +} + +static void accept_cr(struct c4iw_ep *ep, __be32 peer_ip, struct sk_buff *skb, + struct cpl_pass_accept_req *req) +{ + struct cpl_pass_accept_rpl *rpl; + unsigned int mtu_idx; + u64 opt0; + u32 opt2; + int wscale; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + BUG_ON(skb_cloned(skb)); + skb_trim(skb, sizeof(*rpl)); + skb_get(skb); + cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx); + wscale = compute_wscale(rcv_win); + opt0 = KEEP_ALIVE(1) | + WND_SCALE(wscale) | + MSS_IDX(mtu_idx) | + L2T_IDX(ep->l2t->idx) | + TX_CHAN(ep->tx_chan) | + SMAC_SEL(ep->smac_idx) | + DSCP(ep->tos) | + RCV_BUFSIZ(rcv_win>>10); + opt2 = RX_CHANNEL(0) | + RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid); + + if (enable_tcp_timestamps && req->tcpopt.tstamp) + opt2 |= TSTAMPS_EN(1); + if (enable_tcp_sack && req->tcpopt.sack) + opt2 |= SACK_EN(1); + if (wscale && enable_tcp_window_scaling) + opt2 |= WND_SCALE_EN(1); + + rpl = cplhdr(skb); + INIT_TP_WR(rpl, ep->hwtid); + OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL, + ep->hwtid)); + rpl->opt0 = cpu_to_be64(opt0); + rpl->opt2 = cpu_to_be32(opt2); + set_wr_txq(skb, CPL_PRIORITY_SETUP, ep->txq_idx); + c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); + + return; +} + +static void reject_cr(struct c4iw_dev *dev, u32 hwtid, __be32 peer_ip, + struct sk_buff *skb) +{ + PDBG("%s c4iw_dev %p tid %u peer_ip %x\n", __func__, dev, hwtid, + peer_ip); + BUG_ON(skb_cloned(skb)); + skb_trim(skb, sizeof(struct cpl_tid_release)); + skb_get(skb); + release_tid(&dev->rdev, hwtid, skb); + return; +} + +static void get_4tuple(struct cpl_pass_accept_req *req, + __be32 *local_ip, __be32 *peer_ip, + __be16 *local_port, __be16 *peer_port) +{ + int eth_len = G_ETH_HDR_LEN(be32_to_cpu(req->hdr_len)); + int ip_len = G_IP_HDR_LEN(be32_to_cpu(req->hdr_len)); + struct iphdr *ip = (struct iphdr *)((u8 *)(req + 1) + eth_len); + struct tcphdr *tcp = (struct tcphdr *) + ((u8 *)(req + 1) + eth_len + ip_len); + + PDBG("%s saddr 0x%x daddr 0x%x sport %u dport %u\n", __func__, + ntohl(ip->saddr), ntohl(ip->daddr), ntohs(tcp->source), + ntohs(tcp->dest)); + + *peer_ip = ip->saddr; + *local_ip = ip->daddr; + *peer_port = tcp->source; + *local_port = tcp->dest; + + return; +} + +static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *child_ep, *parent_ep; + struct cpl_pass_accept_req *req = cplhdr(skb); + unsigned int stid = GET_POPEN_TID(ntohl(req->tos_stid)); + struct tid_info *t = dev->rdev.lldi.tids; + unsigned int hwtid = GET_TID(req); + struct dst_entry *dst; + struct l2t_entry *l2t; + struct rtable *rt; + __be32 local_ip, peer_ip; + __be16 local_port, peer_port; + struct net_device *pdev; + u32 tx_chan, smac_idx; + u16 rss_qid; + u32 mtu; + int step; + int txq_idx; + + parent_ep = lookup_stid(t, stid); + PDBG("%s parent ep %p tid %u\n", __func__, parent_ep, hwtid); + + get_4tuple(req, &local_ip, &peer_ip, &local_port, &peer_port); + + if (state_read(&parent_ep->com) != LISTEN) { + printk(KERN_ERR "%s - listening ep not in LISTEN\n", + __func__); + goto reject; + } + + /* Find output route */ + rt = find_route(dev, local_ip, peer_ip, local_port, peer_port, + GET_POPEN_TOS(ntohl(req->tos_stid))); + if (!rt) { + printk(KERN_ERR MOD "%s - failed to find dst entry!\n", + __func__); + goto reject; + } + dst = &rt->u.dst; + if (dst->neighbour->dev->flags & IFF_LOOPBACK) { + pdev = ip_dev_find(&init_net, peer_ip); + BUG_ON(!pdev); + l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, dst->neighbour, + pdev, 0); + mtu = pdev->mtu; + tx_chan = cxgb4_port_chan(pdev); + smac_idx = tx_chan << 1; + step = dev->rdev.lldi.ntxq / dev->rdev.lldi.nchan; + txq_idx = cxgb4_port_idx(pdev) * step; + step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan; + rss_qid = dev->rdev.lldi.rxq_ids[cxgb4_port_idx(pdev) * step]; + dev_put(pdev); + } else { + l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, dst->neighbour, + dst->neighbour->dev, 0); + mtu = dst_mtu(dst); + tx_chan = cxgb4_port_chan(dst->neighbour->dev); + smac_idx = tx_chan << 1; + step = dev->rdev.lldi.ntxq / dev->rdev.lldi.nchan; + txq_idx = cxgb4_port_idx(dst->neighbour->dev) * step; + step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan; + rss_qid = dev->rdev.lldi.rxq_ids[ + cxgb4_port_idx(dst->neighbour->dev) * step]; + } + if (!l2t) { + printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", + __func__); + dst_release(dst); + goto reject; + } + + child_ep = alloc_ep(sizeof(*child_ep), GFP_KERNEL); + if (!child_ep) { + printk(KERN_ERR MOD "%s - failed to allocate ep entry!\n", + __func__); + cxgb4_l2t_release(l2t); + dst_release(dst); + goto reject; + } + state_set(&child_ep->com, CONNECTING); + child_ep->com.dev = dev; + child_ep->com.cm_id = NULL; + child_ep->com.local_addr.sin_family = PF_INET; + child_ep->com.local_addr.sin_port = local_port; + child_ep->com.local_addr.sin_addr.s_addr = local_ip; + child_ep->com.remote_addr.sin_family = PF_INET; + child_ep->com.remote_addr.sin_port = peer_port; + child_ep->com.remote_addr.sin_addr.s_addr = peer_ip; + c4iw_get_ep(&parent_ep->com); + child_ep->parent_ep = parent_ep; + child_ep->tos = GET_POPEN_TOS(ntohl(req->tos_stid)); + child_ep->l2t = l2t; + child_ep->dst = dst; + child_ep->hwtid = hwtid; + child_ep->tx_chan = tx_chan; + child_ep->smac_idx = smac_idx; + child_ep->rss_qid = rss_qid; + child_ep->mtu = mtu; + child_ep->txq_idx = txq_idx; + + PDBG("%s tx_chan %u smac_idx %u rss_qid %u\n", __func__, + tx_chan, smac_idx, rss_qid); + + init_timer(&child_ep->timer); + cxgb4_insert_tid(t, child_ep, hwtid); + accept_cr(child_ep, peer_ip, skb, req); + goto out; +reject: + reject_cr(dev, hwtid, peer_ip, skb); +out: + return 0; +} + +static int pass_establish(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *ep; + struct cpl_pass_establish *req = cplhdr(skb); + struct tid_info *t = dev->rdev.lldi.tids; + unsigned int tid = GET_TID(req); + + ep = lookup_tid(t, tid); + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + ep->snd_seq = be32_to_cpu(req->snd_isn); + ep->rcv_seq = be32_to_cpu(req->rcv_isn); + + set_emss(ep, ntohs(req->tcp_opt)); + + dst_confirm(ep->dst); + state_set(&ep->com, MPA_REQ_WAIT); + start_ep_timer(ep); + send_flowc(ep, skb); + + return 0; +} + +static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct cpl_peer_close *hdr = cplhdr(skb); + struct c4iw_ep *ep; + struct c4iw_qp_attributes attrs; + unsigned long flags; + int disconnect = 1; + int release = 0; + int closing = 0; + struct tid_info *t = dev->rdev.lldi.tids; + unsigned int tid = GET_TID(hdr); + int start_timer = 0; + int stop_timer = 0; + + ep = lookup_tid(t, tid); + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + dst_confirm(ep->dst); + + spin_lock_irqsave(&ep->com.lock, flags); + switch (ep->com.state) { + case MPA_REQ_WAIT: + __state_set(&ep->com, CLOSING); + break; + case MPA_REQ_SENT: + __state_set(&ep->com, CLOSING); + connect_reply_upcall(ep, -ECONNRESET); + break; + case MPA_REQ_RCVD: + + /* + * We're gonna mark this puppy DEAD, but keep + * the reference on it until the ULP accepts or + * rejects the CR. Also wake up anyone waiting + * in rdma connection migration (see c4iw_accept_cr()). + */ + __state_set(&ep->com, CLOSING); + ep->com.rpl_done = 1; + ep->com.rpl_err = -ECONNRESET; + PDBG("waking up ep %p tid %u\n", ep, ep->hwtid); + wake_up(&ep->com.waitq); + break; + case MPA_REP_SENT: + __state_set(&ep->com, CLOSING); + ep->com.rpl_done = 1; + ep->com.rpl_err = -ECONNRESET; + PDBG("waking up ep %p tid %u\n", ep, ep->hwtid); + wake_up(&ep->com.waitq); + break; + case FPDU_MODE: + start_timer = 1; + __state_set(&ep->com, CLOSING); + closing = 1; + peer_close_upcall(ep); + break; + case ABORTING: + disconnect = 0; + break; + case CLOSING: + __state_set(&ep->com, MORIBUND); + disconnect = 0; + break; + case MORIBUND: + stop_timer = 1; + if (ep->com.cm_id && ep->com.qp) { + attrs.next_state = C4IW_QP_STATE_IDLE; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + } + close_complete_upcall(ep); + __state_set(&ep->com, DEAD); + release = 1; + disconnect = 0; + break; + case DEAD: + disconnect = 0; + break; + default: + BUG_ON(1); + } + spin_unlock_irqrestore(&ep->com.lock, flags); + if (closing) { + attrs.next_state = C4IW_QP_STATE_CLOSING; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + } + if (start_timer) + start_ep_timer(ep); + if (stop_timer) + stop_ep_timer(ep); + if (disconnect) + c4iw_ep_disconnect(ep, 0, GFP_KERNEL); + if (release) + release_ep_resources(ep); + return 0; +} + +/* + * Returns whether an ABORT_REQ_RSS message is a negative advice. + */ +static int is_neg_adv_abort(unsigned int status) +{ + return status == CPL_ERR_RTX_NEG_ADVICE || + status == CPL_ERR_PERSIST_NEG_ADVICE; +} + +static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct cpl_abort_req_rss *req = cplhdr(skb); + struct c4iw_ep *ep; + struct cpl_abort_rpl *rpl; + struct sk_buff *rpl_skb; + struct c4iw_qp_attributes attrs; + int ret; + int release = 0; + unsigned long flags; + struct tid_info *t = dev->rdev.lldi.tids; + unsigned int tid = GET_TID(req); + int stop_timer = 0; + + ep = lookup_tid(t, tid); + if (is_neg_adv_abort(req->status)) { + PDBG("%s neg_adv_abort ep %p tid %u\n", __func__, ep, + ep->hwtid); + return 0; + } + spin_lock_irqsave(&ep->com.lock, flags); + PDBG("%s ep %p tid %u state %u\n", __func__, ep, ep->hwtid, + ep->com.state); + switch (ep->com.state) { + case CONNECTING: + break; + case MPA_REQ_WAIT: + stop_timer = 1; + break; + case MPA_REQ_SENT: + stop_timer = 1; + connect_reply_upcall(ep, -ECONNRESET); + break; + case MPA_REP_SENT: + ep->com.rpl_done = 1; + ep->com.rpl_err = -ECONNRESET; + PDBG("waking up ep %p\n", ep); + wake_up(&ep->com.waitq); + break; + case MPA_REQ_RCVD: + + /* + * We're gonna mark this puppy DEAD, but keep + * the reference on it until the ULP accepts or + * rejects the CR. Also wake up anyone waiting + * in rdma connection migration (see c4iw_accept_cr()). + */ + ep->com.rpl_done = 1; + ep->com.rpl_err = -ECONNRESET; + PDBG("waking up ep %p tid %u\n", ep, ep->hwtid); + wake_up(&ep->com.waitq); + break; + case MORIBUND: + case CLOSING: + stop_timer = 1; + /*FALLTHROUGH*/ + case FPDU_MODE: + if (ep->com.cm_id && ep->com.qp) { + attrs.next_state = C4IW_QP_STATE_ERROR; + ret = c4iw_modify_qp(ep->com.qp->rhp, + ep->com.qp, C4IW_QP_ATTR_NEXT_STATE, + &attrs, 1); + if (ret) + printk(KERN_ERR MOD + "%s - qp <- error failed!\n", + __func__); + } + peer_abort_upcall(ep); + break; + case ABORTING: + break; + case DEAD: + PDBG("%s PEER_ABORT IN DEAD STATE!!!!\n", __func__); + spin_unlock_irqrestore(&ep->com.lock, flags); + return 0; + default: + BUG_ON(1); + break; + } + dst_confirm(ep->dst); + if (ep->com.state != ABORTING) { + __state_set(&ep->com, DEAD); + release = 1; + } + spin_unlock_irqrestore(&ep->com.lock, flags); + + rpl_skb = get_skb(skb, sizeof(*rpl), GFP_KERNEL); + if (!rpl_skb) { + printk(KERN_ERR MOD "%s - cannot allocate skb!\n", + __func__); + release = 1; + goto out; + } + set_wr_txq(skb, CPL_PRIORITY_DATA, ep->txq_idx); + rpl = (struct cpl_abort_rpl *) skb_put(rpl_skb, sizeof(*rpl)); + INIT_TP_WR(rpl, ep->hwtid); + OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_ABORT_RPL, ep->hwtid)); + rpl->cmd = CPL_ABORT_NO_RST; + c4iw_ofld_send(&ep->com.dev->rdev, rpl_skb); +out: + if (stop_timer) + stop_ep_timer(ep); + if (release) + release_ep_resources(ep); + return 0; +} + +static int close_con_rpl(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *ep; + struct c4iw_qp_attributes attrs; + struct cpl_close_con_rpl *rpl = cplhdr(skb); + unsigned long flags; + int release = 0; + struct tid_info *t = dev->rdev.lldi.tids; + unsigned int tid = GET_TID(rpl); + int stop_timer = 0; + + ep = lookup_tid(t, tid); + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + BUG_ON(!ep); + + /* The cm_id may be null if we failed to connect */ + spin_lock_irqsave(&ep->com.lock, flags); + switch (ep->com.state) { + case CLOSING: + __state_set(&ep->com, MORIBUND); + break; + case MORIBUND: + stop_timer = 1; + if ((ep->com.cm_id) && (ep->com.qp)) { + attrs.next_state = C4IW_QP_STATE_IDLE; + c4iw_modify_qp(ep->com.qp->rhp, + ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, + &attrs, 1); + } + close_complete_upcall(ep); + __state_set(&ep->com, DEAD); + release = 1; + break; + case ABORTING: + case DEAD: + break; + default: + BUG_ON(1); + break; + } + spin_unlock_irqrestore(&ep->com.lock, flags); + if (stop_timer) + stop_ep_timer(ep); + if (release) + release_ep_resources(ep); + return 0; +} + +static int terminate(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *ep; + struct cpl_rdma_terminate *term = cplhdr(skb); + struct tid_info *t = dev->rdev.lldi.tids; + unsigned int tid = GET_TID(term); + + ep = lookup_tid(t, tid); + + if (state_read(&ep->com) != FPDU_MODE) + return 0; + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + skb_pull(skb, sizeof *term); + PDBG("%s saving %d bytes of term msg\n", __func__, skb->len); + skb_copy_from_linear_data(skb, ep->com.qp->attr.terminate_buffer, + skb->len); + ep->com.qp->attr.terminate_msg_len = skb->len; + ep->com.qp->attr.is_terminate_local = 0; + return 0; +} + +/* + * Upcall from the adapter indicating data has been transmitted. + * For us its just the single MPA request or reply. We can now free + * the skb holding the mpa message. + */ +static int fw4_ack(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct c4iw_ep *ep; + struct cpl_fw4_ack *hdr = cplhdr(skb); + u8 credits = hdr->credits; + unsigned int tid = GET_TID(hdr); + struct tid_info *t = dev->rdev.lldi.tids; + + + ep = lookup_tid(t, tid); + PDBG("%s ep %p tid %u credits %u\n", __func__, ep, ep->hwtid, credits); + if (credits == 0) { + PDBG(KERN_ERR "%s 0 credit ack ep %p tid %u state %u\n", + __func__, ep, ep->hwtid, state_read(&ep->com)); + return 0; + } + + dst_confirm(ep->dst); + if (ep->mpa_skb) { + PDBG("%s last streaming msg ack ep %p tid %u state %u " + "initiator %u freeing skb\n", __func__, ep, ep->hwtid, + state_read(&ep->com), ep->mpa_attr.initiator ? 1 : 0); + kfree_skb(ep->mpa_skb); + ep->mpa_skb = NULL; + } + return 0; +} + +int c4iw_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len) +{ + int err; + struct c4iw_ep *ep = to_ep(cm_id); + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + + if (state_read(&ep->com) == DEAD) { + c4iw_put_ep(&ep->com); + return -ECONNRESET; + } + BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD); + if (mpa_rev == 0) + abort_connection(ep, NULL, GFP_KERNEL); + else { + err = send_mpa_reject(ep, pdata, pdata_len); + err = c4iw_ep_disconnect(ep, 0, GFP_KERNEL); + } + c4iw_put_ep(&ep->com); + return 0; +} + +int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) +{ + int err; + struct c4iw_qp_attributes attrs; + enum c4iw_qp_attr_mask mask; + struct c4iw_ep *ep = to_ep(cm_id); + struct c4iw_dev *h = to_c4iw_dev(cm_id->device); + struct c4iw_qp *qp = get_qhp(h, conn_param->qpn); + + PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); + if (state_read(&ep->com) == DEAD) { + err = -ECONNRESET; + goto err; + } + + BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD); + BUG_ON(!qp); + + if ((conn_param->ord > c4iw_max_read_depth) || + (conn_param->ird > c4iw_max_read_depth)) { + abort_connection(ep, NULL, GFP_KERNEL); + err = -EINVAL; + goto err; + } + + cm_id->add_ref(cm_id); + ep->com.cm_id = cm_id; + ep->com.qp = qp; + + ep->ird = conn_param->ird; + ep->ord = conn_param->ord; + + if (peer2peer && ep->ird == 0) + ep->ird = 1; + + PDBG("%s %d ird %d ord %d\n", __func__, __LINE__, ep->ird, ep->ord); + + /* bind QP to EP and move to RTS */ + attrs.mpa_attr = ep->mpa_attr; + attrs.max_ird = ep->ird; + attrs.max_ord = ep->ord; + attrs.llp_stream_handle = ep; + attrs.next_state = C4IW_QP_STATE_RTS; + + /* bind QP and TID with INIT_WR */ + mask = C4IW_QP_ATTR_NEXT_STATE | + C4IW_QP_ATTR_LLP_STREAM_HANDLE | + C4IW_QP_ATTR_MPA_ATTR | + C4IW_QP_ATTR_MAX_IRD | + C4IW_QP_ATTR_MAX_ORD; + + err = c4iw_modify_qp(ep->com.qp->rhp, + ep->com.qp, mask, &attrs, 1); + if (err) + goto err1; + err = send_mpa_reply(ep, conn_param->private_data, + conn_param->private_data_len); + if (err) + goto err1; + + state_set(&ep->com, FPDU_MODE); + established_upcall(ep); + c4iw_put_ep(&ep->com); + return 0; +err1: + ep->com.cm_id = NULL; + ep->com.qp = NULL; + cm_id->rem_ref(cm_id); +err: + c4iw_put_ep(&ep->com); + return err; +} + +int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) +{ + int err = 0; + struct c4iw_dev *dev = to_c4iw_dev(cm_id->device); + struct c4iw_ep *ep; + struct rtable *rt; + struct net_device *pdev; + int step; + + if ((conn_param->ord > c4iw_max_read_depth) || + (conn_param->ird > c4iw_max_read_depth)) { + err = -EINVAL; + goto out; + } + ep = alloc_ep(sizeof(*ep), GFP_KERNEL); + if (!ep) { + printk(KERN_ERR MOD "%s - cannot alloc ep.\n", __func__); + err = -ENOMEM; + goto out; + } + init_timer(&ep->timer); + ep->plen = conn_param->private_data_len; + if (ep->plen) + memcpy(ep->mpa_pkt + sizeof(struct mpa_message), + conn_param->private_data, ep->plen); + ep->ird = conn_param->ird; + ep->ord = conn_param->ord; + + if (peer2peer && ep->ord == 0) + ep->ord = 1; + + cm_id->add_ref(cm_id); + ep->com.dev = dev; + ep->com.cm_id = cm_id; + ep->com.qp = get_qhp(dev, conn_param->qpn); + BUG_ON(!ep->com.qp); + PDBG("%s qpn 0x%x qp %p cm_id %p\n", __func__, conn_param->qpn, + ep->com.qp, cm_id); + + /* + * Allocate an active TID to initiate a TCP connection. + */ + ep->atid = cxgb4_alloc_atid(dev->rdev.lldi.tids, ep); + if (ep->atid == -1) { + printk(KERN_ERR MOD "%s - cannot alloc atid.\n", __func__); + err = -ENOMEM; + goto fail2; + } + + PDBG("%s saddr 0x%x sport 0x%x raddr 0x%x rport 0x%x\n", __func__, + ntohl(cm_id->local_addr.sin_addr.s_addr), + ntohs(cm_id->local_addr.sin_port), + ntohl(cm_id->remote_addr.sin_addr.s_addr), + ntohs(cm_id->remote_addr.sin_port)); + + /* find a route */ + rt = find_route(dev, + cm_id->local_addr.sin_addr.s_addr, + cm_id->remote_addr.sin_addr.s_addr, + cm_id->local_addr.sin_port, + cm_id->remote_addr.sin_port, 0); + if (!rt) { + printk(KERN_ERR MOD "%s - cannot find route.\n", __func__); + err = -EHOSTUNREACH; + goto fail3; + } + ep->dst = &rt->u.dst; + + /* get a l2t entry */ + if (ep->dst->neighbour->dev->flags & IFF_LOOPBACK) { + PDBG("%s LOOPBACK\n", __func__); + pdev = ip_dev_find(&init_net, + cm_id->remote_addr.sin_addr.s_addr); + ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t, + ep->dst->neighbour, + pdev, 0); + ep->mtu = pdev->mtu; + ep->tx_chan = cxgb4_port_chan(pdev); + ep->smac_idx = ep->tx_chan << 1; + step = ep->com.dev->rdev.lldi.ntxq / + ep->com.dev->rdev.lldi.nchan; + ep->txq_idx = cxgb4_port_idx(pdev) * step; + step = ep->com.dev->rdev.lldi.nrxq / + ep->com.dev->rdev.lldi.nchan; + ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[ + cxgb4_port_idx(pdev) * step]; + dev_put(pdev); + } else { + ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t, + ep->dst->neighbour, + ep->dst->neighbour->dev, 0); + ep->mtu = dst_mtu(ep->dst); + ep->tx_chan = cxgb4_port_chan(ep->dst->neighbour->dev); + ep->smac_idx = ep->tx_chan << 1; + step = ep->com.dev->rdev.lldi.ntxq / + ep->com.dev->rdev.lldi.nchan; + ep->txq_idx = cxgb4_port_idx(ep->dst->neighbour->dev) * step; + step = ep->com.dev->rdev.lldi.nrxq / + ep->com.dev->rdev.lldi.nchan; + ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[ + cxgb4_port_idx(ep->dst->neighbour->dev) * step]; + } + if (!ep->l2t) { + printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); + err = -ENOMEM; + goto fail4; + } + + PDBG("%s txq_idx %u tx_chan %u smac_idx %u rss_qid %u l2t_idx %u\n", + __func__, ep->txq_idx, ep->tx_chan, ep->smac_idx, ep->rss_qid, + ep->l2t->idx); + + state_set(&ep->com, CONNECTING); + ep->tos = 0; + ep->com.local_addr = cm_id->local_addr; + ep->com.remote_addr = cm_id->remote_addr; + + /* send connect request to rnic */ + err = send_connect(ep); + if (!err) + goto out; + + cxgb4_l2t_release(ep->l2t); +fail4: + dst_release(ep->dst); +fail3: + cxgb4_free_atid(ep->com.dev->rdev.lldi.tids, ep->atid); +fail2: + cm_id->rem_ref(cm_id); + c4iw_put_ep(&ep->com); +out: + return err; +} + +int c4iw_create_listen(struct iw_cm_id *cm_id, int backlog) +{ + int err = 0; + struct c4iw_dev *dev = to_c4iw_dev(cm_id->device); + struct c4iw_listen_ep *ep; + + + might_sleep(); + + ep = alloc_ep(sizeof(*ep), GFP_KERNEL); + if (!ep) { + printk(KERN_ERR MOD "%s - cannot alloc ep.\n", __func__); + err = -ENOMEM; + goto fail1; + } + PDBG("%s ep %p\n", __func__, ep); + cm_id->add_ref(cm_id); + ep->com.cm_id = cm_id; + ep->com.dev = dev; + ep->backlog = backlog; + ep->com.local_addr = cm_id->local_addr; + + /* + * Allocate a server TID. + */ + ep->stid = cxgb4_alloc_stid(dev->rdev.lldi.tids, PF_INET, ep); + if (ep->stid == -1) { + printk(KERN_ERR MOD "%s - cannot alloc stid.\n", __func__); + err = -ENOMEM; + goto fail2; + } + + state_set(&ep->com, LISTEN); + err = cxgb4_create_server(ep->com.dev->rdev.lldi.ports[0], ep->stid, + ep->com.local_addr.sin_addr.s_addr, + ep->com.local_addr.sin_port, + ep->com.dev->rdev.lldi.rxq_ids[0]); + if (err) + goto fail3; + + /* wait for pass_open_rpl */ + wait_event(ep->com.waitq, ep->com.rpl_done); + err = ep->com.rpl_err; + if (!err) { + cm_id->provider_data = ep; + goto out; + } +fail3: + cxgb4_free_stid(ep->com.dev->rdev.lldi.tids, ep->stid, PF_INET); +fail2: + cm_id->rem_ref(cm_id); + c4iw_put_ep(&ep->com); +fail1: +out: + return err; +} + +int c4iw_destroy_listen(struct iw_cm_id *cm_id) +{ + int err; + struct c4iw_listen_ep *ep = to_listen_ep(cm_id); + + PDBG("%s ep %p\n", __func__, ep); + + might_sleep(); + state_set(&ep->com, DEAD); + ep->com.rpl_done = 0; + ep->com.rpl_err = 0; + err = listen_stop(ep); + if (err) + goto done; + wait_event(ep->com.waitq, ep->com.rpl_done); + cxgb4_free_stid(ep->com.dev->rdev.lldi.tids, ep->stid, PF_INET); +done: + err = ep->com.rpl_err; + cm_id->rem_ref(cm_id); + c4iw_put_ep(&ep->com); + return err; +} + +int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp) +{ + int ret = 0; + unsigned long flags; + int close = 0; + int fatal = 0; + struct c4iw_rdev *rdev; + int start_timer = 0; + int stop_timer = 0; + + spin_lock_irqsave(&ep->com.lock, flags); + + PDBG("%s ep %p state %s, abrupt %d\n", __func__, ep, + states[ep->com.state], abrupt); + + rdev = &ep->com.dev->rdev; + if (c4iw_fatal_error(rdev)) { + fatal = 1; + close_complete_upcall(ep); + ep->com.state = DEAD; + } + switch (ep->com.state) { + case MPA_REQ_WAIT: + case MPA_REQ_SENT: + case MPA_REQ_RCVD: + case MPA_REP_SENT: + case FPDU_MODE: + close = 1; + if (abrupt) + ep->com.state = ABORTING; + else { + ep->com.state = CLOSING; + start_timer = 1; + } + set_bit(CLOSE_SENT, &ep->com.flags); + break; + case CLOSING: + if (!test_and_set_bit(CLOSE_SENT, &ep->com.flags)) { + close = 1; + if (abrupt) { + stop_timer = 1; + ep->com.state = ABORTING; + } else + ep->com.state = MORIBUND; + } + break; + case MORIBUND: + case ABORTING: + case DEAD: + PDBG("%s ignoring disconnect ep %p state %u\n", + __func__, ep, ep->com.state); + break; + default: + BUG(); + break; + } + + spin_unlock_irqrestore(&ep->com.lock, flags); + if (start_timer) + start_ep_timer(ep); + if (stop_timer) + stop_ep_timer(ep); + if (close) { + if (abrupt) + ret = abort_connection(ep, NULL, gfp); + else + ret = send_halfclose(ep, gfp); + if (ret) + fatal = 1; + } + if (fatal) + release_ep_resources(ep); + return ret; +} + +/* + * These are the real handlers that are called from a + * work queue. + */ +static c4iw_handler_func work_handlers[NUM_CPL_CMDS] = { + [CPL_ACT_ESTABLISH] = act_establish, + [CPL_ACT_OPEN_RPL] = act_open_rpl, + [CPL_RX_DATA] = rx_data, + [CPL_ABORT_RPL_RSS] = abort_rpl, + [CPL_ABORT_RPL] = abort_rpl, + [CPL_PASS_OPEN_RPL] = pass_open_rpl, + [CPL_CLOSE_LISTSRV_RPL] = close_listsrv_rpl, + [CPL_PASS_ACCEPT_REQ] = pass_accept_req, + [CPL_PASS_ESTABLISH] = pass_establish, + [CPL_PEER_CLOSE] = peer_close, + [CPL_ABORT_REQ_RSS] = peer_abort, + [CPL_CLOSE_CON_RPL] = close_con_rpl, + [CPL_RDMA_TERMINATE] = terminate, + [CPL_FW4_ACK] = fw4_ack +}; + +static void process_timeout(struct c4iw_ep *ep) +{ + struct c4iw_qp_attributes attrs; + int abort = 1; + + spin_lock_irq(&ep->com.lock); + PDBG("%s ep %p tid %u state %d\n", __func__, ep, ep->hwtid, + ep->com.state); + switch (ep->com.state) { + case MPA_REQ_SENT: + __state_set(&ep->com, ABORTING); + connect_reply_upcall(ep, -ETIMEDOUT); + break; + case MPA_REQ_WAIT: + __state_set(&ep->com, ABORTING); + break; + case CLOSING: + case MORIBUND: + if (ep->com.cm_id && ep->com.qp) { + attrs.next_state = C4IW_QP_STATE_ERROR; + c4iw_modify_qp(ep->com.qp->rhp, + ep->com.qp, C4IW_QP_ATTR_NEXT_STATE, + &attrs, 1); + } + __state_set(&ep->com, ABORTING); + break; + default: + printk(KERN_ERR "%s unexpected state ep %p tid %u state %u\n", + __func__, ep, ep->hwtid, ep->com.state); + WARN_ON(1); + abort = 0; + } + spin_unlock_irq(&ep->com.lock); + if (abort) + abort_connection(ep, NULL, GFP_KERNEL); + c4iw_put_ep(&ep->com); +} + +static void process_timedout_eps(void) +{ + struct c4iw_ep *ep; + + spin_lock_irq(&timeout_lock); + while (!list_empty(&timeout_list)) { + struct list_head *tmp; + + tmp = timeout_list.next; + list_del(tmp); + spin_unlock_irq(&timeout_lock); + ep = list_entry(tmp, struct c4iw_ep, entry); + process_timeout(ep); + spin_lock_irq(&timeout_lock); + } + spin_unlock_irq(&timeout_lock); +} + +static void process_work(struct work_struct *work) +{ + struct sk_buff *skb = NULL; + struct c4iw_dev *dev; + struct cpl_act_establish *rpl = cplhdr(skb); + unsigned int opcode; + int ret; + + while ((skb = skb_dequeue(&rxq))) { + rpl = cplhdr(skb); + dev = *((struct c4iw_dev **) (skb->cb + sizeof(void *))); + opcode = rpl->ot.opcode; + + BUG_ON(!work_handlers[opcode]); + ret = work_handlers[opcode](dev, skb); + if (!ret) + kfree_skb(skb); + } + process_timedout_eps(); +} + +static DECLARE_WORK(skb_work, process_work); + +static void ep_timeout(unsigned long arg) +{ + struct c4iw_ep *ep = (struct c4iw_ep *)arg; + + spin_lock(&timeout_lock); + list_add_tail(&ep->entry, &timeout_list); + spin_unlock(&timeout_lock); + queue_work(workq, &skb_work); +} + +/* + * All the CM events are handled on a work queue to have a safe context. + */ +static int sched(struct c4iw_dev *dev, struct sk_buff *skb) +{ + + /* + * Save dev in the skb->cb area. + */ + *((struct c4iw_dev **) (skb->cb + sizeof(void *))) = dev; + + /* + * Queue the skb and schedule the worker thread. + */ + skb_queue_tail(&rxq, skb); + queue_work(workq, &skb_work); + return 0; +} + +static int set_tcb_rpl(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct cpl_set_tcb_rpl *rpl = cplhdr(skb); + + if (rpl->status != CPL_ERR_NONE) { + printk(KERN_ERR MOD "Unexpected SET_TCB_RPL status %u " + "for tid %u\n", rpl->status, GET_TID(rpl)); + } + return 0; +} + +static int fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb) +{ + struct cpl_fw6_msg *rpl = cplhdr(skb); + struct c4iw_wr_wait *wr_waitp; + int ret; + + PDBG("%s type %u\n", __func__, rpl->type); + + switch (rpl->type) { + case 1: + ret = (int)((be64_to_cpu(rpl->data[0]) >> 8) & 0xff); + wr_waitp = (__force struct c4iw_wr_wait *)rpl->data[1]; + PDBG("%s wr_waitp %p ret %u\n", __func__, wr_waitp, ret); + if (wr_waitp) { + wr_waitp->ret = ret; + wr_waitp->done = 1; + wake_up(&wr_waitp->wait); + } + break; + case 2: + c4iw_ev_dispatch(dev, (struct t4_cqe *)&rpl->data[0]); + break; + default: + printk(KERN_ERR MOD "%s unexpected fw6 msg type %u\n", __func__, + rpl->type); + break; + } + return 0; +} + +/* + * Most upcalls from the T4 Core go to sched() to + * schedule the processing on a work queue. + */ +c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS] = { + [CPL_ACT_ESTABLISH] = sched, + [CPL_ACT_OPEN_RPL] = sched, + [CPL_RX_DATA] = sched, + [CPL_ABORT_RPL_RSS] = sched, + [CPL_ABORT_RPL] = sched, + [CPL_PASS_OPEN_RPL] = sched, + [CPL_CLOSE_LISTSRV_RPL] = sched, + [CPL_PASS_ACCEPT_REQ] = sched, + [CPL_PASS_ESTABLISH] = sched, + [CPL_PEER_CLOSE] = sched, + [CPL_CLOSE_CON_RPL] = sched, + [CPL_ABORT_REQ_RSS] = sched, + [CPL_RDMA_TERMINATE] = sched, + [CPL_FW4_ACK] = sched, + [CPL_SET_TCB_RPL] = set_tcb_rpl, + [CPL_FW6_MSG] = fw6_msg +}; + +int __init c4iw_cm_init(void) +{ + spin_lock_init(&timeout_lock); + skb_queue_head_init(&rxq); + + workq = create_singlethread_workqueue("iw_cxgb4"); + if (!workq) + return -ENOMEM; + + return 0; +} + +void __exit c4iw_cm_term(void) +{ + WARN_ON(!list_empty(&timeout_list)); + flush_workqueue(workq); + destroy_workqueue(workq); +} diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c new file mode 100644 index 000000000000..fb1aafcc294f --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/cq.c @@ -0,0 +1,882 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + */ + +#include "iw_cxgb4.h" + +static int destroy_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, + struct c4iw_dev_ucontext *uctx) +{ + struct fw_ri_res_wr *res_wr; + struct fw_ri_res *res; + int wr_len; + struct c4iw_wr_wait wr_wait; + struct sk_buff *skb; + int ret; + + wr_len = sizeof *res_wr + sizeof *res; + skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL); + if (!skb) + return -ENOMEM; + set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); + + res_wr = (struct fw_ri_res_wr *)__skb_put(skb, wr_len); + memset(res_wr, 0, wr_len); + res_wr->op_nres = cpu_to_be32( + FW_WR_OP(FW_RI_RES_WR) | + V_FW_RI_RES_WR_NRES(1) | + FW_WR_COMPL(1)); + res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); + res_wr->cookie = (u64)&wr_wait; + res = res_wr->res; + res->u.cq.restype = FW_RI_RES_TYPE_CQ; + res->u.cq.op = FW_RI_RES_OP_RESET; + res->u.cq.iqid = cpu_to_be32(cq->cqid); + + c4iw_init_wr_wait(&wr_wait); + ret = c4iw_ofld_send(rdev, skb); + if (!ret) { + wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); + if (!wr_wait.done) { + printk(KERN_ERR MOD "Device %s not responding!\n", + pci_name(rdev->lldi.pdev)); + rdev->flags = T4_FATAL_ERROR; + ret = -EIO; + } else + ret = wr_wait.ret; + } + + kfree(cq->sw_queue); + dma_free_coherent(&(rdev->lldi.pdev->dev), + cq->memsize, cq->queue, + pci_unmap_addr(cq, mapping)); + c4iw_put_cqid(rdev, cq->cqid, uctx); + return ret; +} + +static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, + struct c4iw_dev_ucontext *uctx) +{ + struct fw_ri_res_wr *res_wr; + struct fw_ri_res *res; + int wr_len; + int user = (uctx != &rdev->uctx); + struct c4iw_wr_wait wr_wait; + int ret; + struct sk_buff *skb; + + cq->cqid = c4iw_get_cqid(rdev, uctx); + if (!cq->cqid) { + ret = -ENOMEM; + goto err1; + } + + if (!user) { + cq->sw_queue = kzalloc(cq->memsize, GFP_KERNEL); + if (!cq->sw_queue) { + ret = -ENOMEM; + goto err2; + } + } + cq->queue = dma_alloc_coherent(&rdev->lldi.pdev->dev, cq->memsize, + &cq->dma_addr, GFP_KERNEL); + if (!cq->queue) { + ret = -ENOMEM; + goto err3; + } + pci_unmap_addr_set(cq, mapping, cq->dma_addr); + memset(cq->queue, 0, cq->memsize); + + /* build fw_ri_res_wr */ + wr_len = sizeof *res_wr + sizeof *res; + + skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL); + if (!skb) { + ret = -ENOMEM; + goto err4; + } + set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); + + res_wr = (struct fw_ri_res_wr *)__skb_put(skb, wr_len); + memset(res_wr, 0, wr_len); + res_wr->op_nres = cpu_to_be32( + FW_WR_OP(FW_RI_RES_WR) | + V_FW_RI_RES_WR_NRES(1) | + FW_WR_COMPL(1)); + res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); + res_wr->cookie = (u64)&wr_wait; + res = res_wr->res; + res->u.cq.restype = FW_RI_RES_TYPE_CQ; + res->u.cq.op = FW_RI_RES_OP_WRITE; + res->u.cq.iqid = cpu_to_be32(cq->cqid); + res->u.cq.iqandst_to_iqandstindex = cpu_to_be32( + V_FW_RI_RES_WR_IQANUS(0) | + V_FW_RI_RES_WR_IQANUD(1) | + F_FW_RI_RES_WR_IQANDST | + V_FW_RI_RES_WR_IQANDSTINDEX(*rdev->lldi.rxq_ids)); + res->u.cq.iqdroprss_to_iqesize = cpu_to_be16( + F_FW_RI_RES_WR_IQDROPRSS | + V_FW_RI_RES_WR_IQPCIECH(2) | + V_FW_RI_RES_WR_IQINTCNTTHRESH(0) | + F_FW_RI_RES_WR_IQO | + V_FW_RI_RES_WR_IQESIZE(1)); + res->u.cq.iqsize = cpu_to_be16(cq->size); + res->u.cq.iqaddr = cpu_to_be64(cq->dma_addr); + + c4iw_init_wr_wait(&wr_wait); + + ret = c4iw_ofld_send(rdev, skb); + if (ret) + goto err4; + PDBG("%s wait_event wr_wait %p\n", __func__, &wr_wait); + wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); + if (!wr_wait.done) { + printk(KERN_ERR MOD "Device %s not responding!\n", + pci_name(rdev->lldi.pdev)); + rdev->flags = T4_FATAL_ERROR; + ret = -EIO; + } else + ret = wr_wait.ret; + if (ret) + goto err4; + + cq->gen = 1; + cq->gts = rdev->lldi.gts_reg; + cq->rdev = rdev; + if (user) { + cq->ugts = (u64)pci_resource_start(rdev->lldi.pdev, 2) + + (cq->cqid << rdev->cqshift); + cq->ugts &= PAGE_MASK; + } + return 0; +err4: + dma_free_coherent(&rdev->lldi.pdev->dev, cq->memsize, cq->queue, + pci_unmap_addr(cq, mapping)); +err3: + kfree(cq->sw_queue); +err2: + c4iw_put_cqid(rdev, cq->cqid, uctx); +err1: + return ret; +} + +static void insert_recv_cqe(struct t4_wq *wq, struct t4_cq *cq) +{ + struct t4_cqe cqe; + + PDBG("%s wq %p cq %p sw_cidx %u sw_pidx %u\n", __func__, + wq, cq, cq->sw_cidx, cq->sw_pidx); + memset(&cqe, 0, sizeof(cqe)); + cqe.header = cpu_to_be32(V_CQE_STATUS(T4_ERR_SWFLUSH) | + V_CQE_OPCODE(FW_RI_SEND) | + V_CQE_TYPE(0) | + V_CQE_SWCQE(1) | + V_CQE_QPID(wq->rq.qid)); + cqe.bits_type_ts = cpu_to_be64(V_CQE_GENBIT((u64)cq->gen)); + cq->sw_queue[cq->sw_pidx] = cqe; + t4_swcq_produce(cq); +} + +int c4iw_flush_rq(struct t4_wq *wq, struct t4_cq *cq, int count) +{ + int flushed = 0; + int in_use = wq->rq.in_use - count; + + BUG_ON(in_use < 0); + PDBG("%s wq %p cq %p rq.in_use %u skip count %u\n", __func__, + wq, cq, wq->rq.in_use, count); + while (in_use--) { + insert_recv_cqe(wq, cq); + flushed++; + } + return flushed; +} + +static void insert_sq_cqe(struct t4_wq *wq, struct t4_cq *cq, + struct t4_swsqe *swcqe) +{ + struct t4_cqe cqe; + + PDBG("%s wq %p cq %p sw_cidx %u sw_pidx %u\n", __func__, + wq, cq, cq->sw_cidx, cq->sw_pidx); + memset(&cqe, 0, sizeof(cqe)); + cqe.header = cpu_to_be32(V_CQE_STATUS(T4_ERR_SWFLUSH) | + V_CQE_OPCODE(swcqe->opcode) | + V_CQE_TYPE(1) | + V_CQE_SWCQE(1) | + V_CQE_QPID(wq->sq.qid)); + CQE_WRID_SQ_IDX(&cqe) = swcqe->idx; + cqe.bits_type_ts = cpu_to_be64(V_CQE_GENBIT((u64)cq->gen)); + cq->sw_queue[cq->sw_pidx] = cqe; + t4_swcq_produce(cq); +} + +int c4iw_flush_sq(struct t4_wq *wq, struct t4_cq *cq, int count) +{ + int flushed = 0; + struct t4_swsqe *swsqe = &wq->sq.sw_sq[wq->sq.cidx + count]; + int in_use = wq->sq.in_use - count; + + BUG_ON(in_use < 0); + while (in_use--) { + swsqe->signaled = 0; + insert_sq_cqe(wq, cq, swsqe); + swsqe++; + if (swsqe == (wq->sq.sw_sq + wq->sq.size)) + swsqe = wq->sq.sw_sq; + flushed++; + } + return flushed; +} + +/* + * Move all CQEs from the HWCQ into the SWCQ. + */ +void c4iw_flush_hw_cq(struct t4_cq *cq) +{ + struct t4_cqe *cqe = NULL, *swcqe; + int ret; + + PDBG("%s cq %p cqid 0x%x\n", __func__, cq, cq->cqid); + ret = t4_next_hw_cqe(cq, &cqe); + while (!ret) { + PDBG("%s flushing hwcq cidx 0x%x swcq pidx 0x%x\n", + __func__, cq->cidx, cq->sw_pidx); + swcqe = &cq->sw_queue[cq->sw_pidx]; + *swcqe = *cqe; + swcqe->header |= cpu_to_be32(V_CQE_SWCQE(1)); + t4_swcq_produce(cq); + t4_hwcq_consume(cq); + ret = t4_next_hw_cqe(cq, &cqe); + } +} + +static int cqe_completes_wr(struct t4_cqe *cqe, struct t4_wq *wq) +{ + if (CQE_OPCODE(cqe) == FW_RI_TERMINATE) + return 0; + + if ((CQE_OPCODE(cqe) == FW_RI_RDMA_WRITE) && RQ_TYPE(cqe)) + return 0; + + if ((CQE_OPCODE(cqe) == FW_RI_READ_RESP) && SQ_TYPE(cqe)) + return 0; + + if (CQE_SEND_OPCODE(cqe) && RQ_TYPE(cqe) && t4_rq_empty(wq)) + return 0; + return 1; +} + +void c4iw_count_scqes(struct t4_cq *cq, struct t4_wq *wq, int *count) +{ + struct t4_cqe *cqe; + u32 ptr; + + *count = 0; + ptr = cq->sw_cidx; + while (ptr != cq->sw_pidx) { + cqe = &cq->sw_queue[ptr]; + if ((SQ_TYPE(cqe) || ((CQE_OPCODE(cqe) == FW_RI_READ_RESP) && + wq->sq.oldest_read)) && + (CQE_QPID(cqe) == wq->sq.qid)) + (*count)++; + if (++ptr == cq->size) + ptr = 0; + } + PDBG("%s cq %p count %d\n", __func__, cq, *count); +} + +void c4iw_count_rcqes(struct t4_cq *cq, struct t4_wq *wq, int *count) +{ + struct t4_cqe *cqe; + u32 ptr; + + *count = 0; + PDBG("%s count zero %d\n", __func__, *count); + ptr = cq->sw_cidx; + while (ptr != cq->sw_pidx) { + cqe = &cq->sw_queue[ptr]; + if (RQ_TYPE(cqe) && (CQE_OPCODE(cqe) != FW_RI_READ_RESP) && + (CQE_QPID(cqe) == wq->rq.qid) && cqe_completes_wr(cqe, wq)) + (*count)++; + if (++ptr == cq->size) + ptr = 0; + } + PDBG("%s cq %p count %d\n", __func__, cq, *count); +} + +static void flush_completed_wrs(struct t4_wq *wq, struct t4_cq *cq) +{ + struct t4_swsqe *swsqe; + u16 ptr = wq->sq.cidx; + int count = wq->sq.in_use; + int unsignaled = 0; + + swsqe = &wq->sq.sw_sq[ptr]; + while (count--) + if (!swsqe->signaled) { + if (++ptr == wq->sq.size) + ptr = 0; + swsqe = &wq->sq.sw_sq[ptr]; + unsignaled++; + } else if (swsqe->complete) { + + /* + * Insert this completed cqe into the swcq. + */ + PDBG("%s moving cqe into swcq sq idx %u cq idx %u\n", + __func__, ptr, cq->sw_pidx); + swsqe->cqe.header |= htonl(V_CQE_SWCQE(1)); + cq->sw_queue[cq->sw_pidx] = swsqe->cqe; + t4_swcq_produce(cq); + swsqe->signaled = 0; + wq->sq.in_use -= unsignaled; + break; + } else + break; +} + +static void create_read_req_cqe(struct t4_wq *wq, struct t4_cqe *hw_cqe, + struct t4_cqe *read_cqe) +{ + read_cqe->u.scqe.cidx = wq->sq.oldest_read->idx; + read_cqe->len = cpu_to_be32(wq->sq.oldest_read->read_len); + read_cqe->header = htonl(V_CQE_QPID(CQE_QPID(hw_cqe)) | + V_CQE_SWCQE(SW_CQE(hw_cqe)) | + V_CQE_OPCODE(FW_RI_READ_REQ) | + V_CQE_TYPE(1)); +} + +/* + * Return a ptr to the next read wr in the SWSQ or NULL. + */ +static void advance_oldest_read(struct t4_wq *wq) +{ + + u32 rptr = wq->sq.oldest_read - wq->sq.sw_sq + 1; + + if (rptr == wq->sq.size) + rptr = 0; + while (rptr != wq->sq.pidx) { + wq->sq.oldest_read = &wq->sq.sw_sq[rptr]; + + if (wq->sq.oldest_read->opcode == FW_RI_READ_REQ) + return; + if (++rptr == wq->sq.size) + rptr = 0; + } + wq->sq.oldest_read = NULL; +} + +/* + * poll_cq + * + * Caller must: + * check the validity of the first CQE, + * supply the wq assicated with the qpid. + * + * credit: cq credit to return to sge. + * cqe_flushed: 1 iff the CQE is flushed. + * cqe: copy of the polled CQE. + * + * return value: + * 0 CQE returned ok. + * -EAGAIN CQE skipped, try again. + * -EOVERFLOW CQ overflow detected. + */ +static int poll_cq(struct t4_wq *wq, struct t4_cq *cq, struct t4_cqe *cqe, + u8 *cqe_flushed, u64 *cookie, u32 *credit) +{ + int ret = 0; + struct t4_cqe *hw_cqe, read_cqe; + + *cqe_flushed = 0; + *credit = 0; + ret = t4_next_cqe(cq, &hw_cqe); + if (ret) + return ret; + + PDBG("%s CQE OVF %u qpid 0x%0x genbit %u type %u status 0x%0x" + " opcode 0x%0x len 0x%0x wrid_hi_stag 0x%x wrid_low_msn 0x%x\n", + __func__, CQE_OVFBIT(hw_cqe), CQE_QPID(hw_cqe), + CQE_GENBIT(hw_cqe), CQE_TYPE(hw_cqe), CQE_STATUS(hw_cqe), + CQE_OPCODE(hw_cqe), CQE_LEN(hw_cqe), CQE_WRID_HI(hw_cqe), + CQE_WRID_LOW(hw_cqe)); + + /* + * skip cqe's not affiliated with a QP. + */ + if (wq == NULL) { + ret = -EAGAIN; + goto skip_cqe; + } + + /* + * Gotta tweak READ completions: + * 1) the cqe doesn't contain the sq_wptr from the wr. + * 2) opcode not reflected from the wr. + * 3) read_len not reflected from the wr. + * 4) cq_type is RQ_TYPE not SQ_TYPE. + */ + if (RQ_TYPE(hw_cqe) && (CQE_OPCODE(hw_cqe) == FW_RI_READ_RESP)) { + + /* + * If this is an unsolicited read response, then the read + * was generated by the kernel driver as part of peer-2-peer + * connection setup. So ignore the completion. + */ + if (!wq->sq.oldest_read) { + if (CQE_STATUS(hw_cqe)) + t4_set_wq_in_error(wq); + ret = -EAGAIN; + goto skip_cqe; + } + + /* + * Don't write to the HWCQ, so create a new read req CQE + * in local memory. + */ + create_read_req_cqe(wq, hw_cqe, &read_cqe); + hw_cqe = &read_cqe; + advance_oldest_read(wq); + } + + if (CQE_STATUS(hw_cqe) || t4_wq_in_error(wq)) { + *cqe_flushed = t4_wq_in_error(wq); + t4_set_wq_in_error(wq); + goto proc_cqe; + } + + /* + * RECV completion. + */ + if (RQ_TYPE(hw_cqe)) { + + /* + * HW only validates 4 bits of MSN. So we must validate that + * the MSN in the SEND is the next expected MSN. If its not, + * then we complete this with T4_ERR_MSN and mark the wq in + * error. + */ + + if (t4_rq_empty(wq)) { + t4_set_wq_in_error(wq); + ret = -EAGAIN; + goto skip_cqe; + } + if (unlikely((CQE_WRID_MSN(hw_cqe) != (wq->rq.msn)))) { + t4_set_wq_in_error(wq); + hw_cqe->header |= htonl(V_CQE_STATUS(T4_ERR_MSN)); + goto proc_cqe; + } + goto proc_cqe; + } + + /* + * If we get here its a send completion. + * + * Handle out of order completion. These get stuffed + * in the SW SQ. Then the SW SQ is walked to move any + * now in-order completions into the SW CQ. This handles + * 2 cases: + * 1) reaping unsignaled WRs when the first subsequent + * signaled WR is completed. + * 2) out of order read completions. + */ + if (!SW_CQE(hw_cqe) && (CQE_WRID_SQ_IDX(hw_cqe) != wq->sq.cidx)) { + struct t4_swsqe *swsqe; + + PDBG("%s out of order completion going in sw_sq at idx %u\n", + __func__, CQE_WRID_SQ_IDX(hw_cqe)); + swsqe = &wq->sq.sw_sq[CQE_WRID_SQ_IDX(hw_cqe)]; + swsqe->cqe = *hw_cqe; + swsqe->complete = 1; + ret = -EAGAIN; + goto flush_wq; + } + +proc_cqe: + *cqe = *hw_cqe; + + /* + * Reap the associated WR(s) that are freed up with this + * completion. + */ + if (SQ_TYPE(hw_cqe)) { + wq->sq.cidx = CQE_WRID_SQ_IDX(hw_cqe); + PDBG("%s completing sq idx %u\n", __func__, wq->sq.cidx); + *cookie = wq->sq.sw_sq[wq->sq.cidx].wr_id; + t4_sq_consume(wq); + } else { + PDBG("%s completing rq idx %u\n", __func__, wq->rq.cidx); + *cookie = wq->rq.sw_rq[wq->rq.cidx].wr_id; + BUG_ON(t4_rq_empty(wq)); + t4_rq_consume(wq); + } + +flush_wq: + /* + * Flush any completed cqes that are now in-order. + */ + flush_completed_wrs(wq, cq); + +skip_cqe: + if (SW_CQE(hw_cqe)) { + PDBG("%s cq %p cqid 0x%x skip sw cqe cidx %u\n", + __func__, cq, cq->cqid, cq->sw_cidx); + t4_swcq_consume(cq); + } else { + PDBG("%s cq %p cqid 0x%x skip hw cqe cidx %u\n", + __func__, cq, cq->cqid, cq->cidx); + t4_hwcq_consume(cq); + } + return ret; +} + +/* + * Get one cq entry from c4iw and map it to openib. + * + * Returns: + * 0 cqe returned + * -ENODATA EMPTY; + * -EAGAIN caller must try again + * any other -errno fatal error + */ +static int c4iw_poll_cq_one(struct c4iw_cq *chp, struct ib_wc *wc) +{ + struct c4iw_qp *qhp = NULL; + struct t4_cqe cqe = {0, 0}, *rd_cqe; + struct t4_wq *wq; + u32 credit = 0; + u8 cqe_flushed; + u64 cookie = 0; + int ret; + + ret = t4_next_cqe(&chp->cq, &rd_cqe); + + if (ret) + return ret; + + qhp = get_qhp(chp->rhp, CQE_QPID(rd_cqe)); + if (!qhp) + wq = NULL; + else { + spin_lock(&qhp->lock); + wq = &(qhp->wq); + } + ret = poll_cq(wq, &(chp->cq), &cqe, &cqe_flushed, &cookie, &credit); + if (ret) + goto out; + + wc->wr_id = cookie; + wc->qp = &qhp->ibqp; + wc->vendor_err = CQE_STATUS(&cqe); + wc->wc_flags = 0; + + PDBG("%s qpid 0x%x type %d opcode %d status 0x%x len %u wrid hi 0x%x " + "lo 0x%x cookie 0x%llx\n", __func__, CQE_QPID(&cqe), + CQE_TYPE(&cqe), CQE_OPCODE(&cqe), CQE_STATUS(&cqe), CQE_LEN(&cqe), + CQE_WRID_HI(&cqe), CQE_WRID_LOW(&cqe), (unsigned long long)cookie); + + if (CQE_TYPE(&cqe) == 0) { + if (!CQE_STATUS(&cqe)) + wc->byte_len = CQE_LEN(&cqe); + else + wc->byte_len = 0; + wc->opcode = IB_WC_RECV; + if (CQE_OPCODE(&cqe) == FW_RI_SEND_WITH_INV || + CQE_OPCODE(&cqe) == FW_RI_SEND_WITH_SE_INV) { + wc->ex.invalidate_rkey = CQE_WRID_STAG(&cqe); + wc->wc_flags |= IB_WC_WITH_INVALIDATE; + } + } else { + switch (CQE_OPCODE(&cqe)) { + case FW_RI_RDMA_WRITE: + wc->opcode = IB_WC_RDMA_WRITE; + break; + case FW_RI_READ_REQ: + wc->opcode = IB_WC_RDMA_READ; + wc->byte_len = CQE_LEN(&cqe); + break; + case FW_RI_SEND_WITH_INV: + case FW_RI_SEND_WITH_SE_INV: + wc->opcode = IB_WC_SEND; + wc->wc_flags |= IB_WC_WITH_INVALIDATE; + break; + case FW_RI_SEND: + case FW_RI_SEND_WITH_SE: + wc->opcode = IB_WC_SEND; + break; + case FW_RI_BIND_MW: + wc->opcode = IB_WC_BIND_MW; + break; + + case FW_RI_LOCAL_INV: + wc->opcode = IB_WC_LOCAL_INV; + break; + case FW_RI_FAST_REGISTER: + wc->opcode = IB_WC_FAST_REG_MR; + break; + default: + printk(KERN_ERR MOD "Unexpected opcode %d " + "in the CQE received for QPID=0x%0x\n", + CQE_OPCODE(&cqe), CQE_QPID(&cqe)); + ret = -EINVAL; + goto out; + } + } + + if (cqe_flushed) + wc->status = IB_WC_WR_FLUSH_ERR; + else { + + switch (CQE_STATUS(&cqe)) { + case T4_ERR_SUCCESS: + wc->status = IB_WC_SUCCESS; + break; + case T4_ERR_STAG: + wc->status = IB_WC_LOC_ACCESS_ERR; + break; + case T4_ERR_PDID: + wc->status = IB_WC_LOC_PROT_ERR; + break; + case T4_ERR_QPID: + case T4_ERR_ACCESS: + wc->status = IB_WC_LOC_ACCESS_ERR; + break; + case T4_ERR_WRAP: + wc->status = IB_WC_GENERAL_ERR; + break; + case T4_ERR_BOUND: + wc->status = IB_WC_LOC_LEN_ERR; + break; + case T4_ERR_INVALIDATE_SHARED_MR: + case T4_ERR_INVALIDATE_MR_WITH_MW_BOUND: + wc->status = IB_WC_MW_BIND_ERR; + break; + case T4_ERR_CRC: + case T4_ERR_MARKER: + case T4_ERR_PDU_LEN_ERR: + case T4_ERR_OUT_OF_RQE: + case T4_ERR_DDP_VERSION: + case T4_ERR_RDMA_VERSION: + case T4_ERR_DDP_QUEUE_NUM: + case T4_ERR_MSN: + case T4_ERR_TBIT: + case T4_ERR_MO: + case T4_ERR_MSN_RANGE: + case T4_ERR_IRD_OVERFLOW: + case T4_ERR_OPCODE: + wc->status = IB_WC_FATAL_ERR; + break; + case T4_ERR_SWFLUSH: + wc->status = IB_WC_WR_FLUSH_ERR; + break; + default: + printk(KERN_ERR MOD + "Unexpected cqe_status 0x%x for QPID=0x%0x\n", + CQE_STATUS(&cqe), CQE_QPID(&cqe)); + ret = -EINVAL; + } + } +out: + if (wq) + spin_unlock(&qhp->lock); + return ret; +} + +int c4iw_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) +{ + struct c4iw_cq *chp; + unsigned long flags; + int npolled; + int err = 0; + + chp = to_c4iw_cq(ibcq); + + spin_lock_irqsave(&chp->lock, flags); + for (npolled = 0; npolled < num_entries; ++npolled) { + do { + err = c4iw_poll_cq_one(chp, wc + npolled); + } while (err == -EAGAIN); + if (err) + break; + } + spin_unlock_irqrestore(&chp->lock, flags); + return !err || err == -ENODATA ? npolled : err; +} + +int c4iw_destroy_cq(struct ib_cq *ib_cq) +{ + struct c4iw_cq *chp; + struct c4iw_ucontext *ucontext; + + PDBG("%s ib_cq %p\n", __func__, ib_cq); + chp = to_c4iw_cq(ib_cq); + + remove_handle(chp->rhp, &chp->rhp->cqidr, chp->cq.cqid); + atomic_dec(&chp->refcnt); + wait_event(chp->wait, !atomic_read(&chp->refcnt)); + + ucontext = ib_cq->uobject ? to_c4iw_ucontext(ib_cq->uobject->context) + : NULL; + destroy_cq(&chp->rhp->rdev, &chp->cq, + ucontext ? &ucontext->uctx : &chp->cq.rdev->uctx); + kfree(chp); + return 0; +} + +struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries, + int vector, struct ib_ucontext *ib_context, + struct ib_udata *udata) +{ + struct c4iw_dev *rhp; + struct c4iw_cq *chp; + struct c4iw_create_cq_resp uresp; + struct c4iw_ucontext *ucontext = NULL; + int ret; + size_t memsize; + struct c4iw_mm_entry *mm, *mm2; + + PDBG("%s ib_dev %p entries %d\n", __func__, ibdev, entries); + + rhp = to_c4iw_dev(ibdev); + + chp = kzalloc(sizeof(*chp), GFP_KERNEL); + if (!chp) + return ERR_PTR(-ENOMEM); + + if (ib_context) + ucontext = to_c4iw_ucontext(ib_context); + + /* account for the status page. */ + entries++; + + /* + * entries must be multiple of 16 for HW. + */ + entries = roundup(entries, 16); + memsize = entries * sizeof *chp->cq.queue; + + /* + * memsize must be a multiple of the page size if its a user cq. + */ + if (ucontext) + memsize = roundup(memsize, PAGE_SIZE); + chp->cq.size = entries; + chp->cq.memsize = memsize; + + ret = create_cq(&rhp->rdev, &chp->cq, + ucontext ? &ucontext->uctx : &rhp->rdev.uctx); + if (ret) + goto err1; + + chp->rhp = rhp; + chp->cq.size--; /* status page */ + chp->ibcq.cqe = chp->cq.size; + spin_lock_init(&chp->lock); + atomic_set(&chp->refcnt, 1); + init_waitqueue_head(&chp->wait); + ret = insert_handle(rhp, &rhp->cqidr, chp, chp->cq.cqid); + if (ret) + goto err2; + + if (ucontext) { + mm = kmalloc(sizeof *mm, GFP_KERNEL); + if (!mm) + goto err3; + mm2 = kmalloc(sizeof *mm2, GFP_KERNEL); + if (!mm2) + goto err4; + + uresp.qid_mask = rhp->rdev.cqmask; + uresp.cqid = chp->cq.cqid; + uresp.size = chp->cq.size; + uresp.memsize = chp->cq.memsize; + spin_lock(&ucontext->mmap_lock); + uresp.key = ucontext->key; + ucontext->key += PAGE_SIZE; + uresp.gts_key = ucontext->key; + ucontext->key += PAGE_SIZE; + spin_unlock(&ucontext->mmap_lock); + ret = ib_copy_to_udata(udata, &uresp, sizeof uresp); + if (ret) + goto err5; + + mm->key = uresp.key; + mm->addr = virt_to_phys(chp->cq.queue); + mm->len = chp->cq.memsize; + insert_mmap(ucontext, mm); + + mm2->key = uresp.gts_key; + mm2->addr = chp->cq.ugts; + mm2->len = PAGE_SIZE; + insert_mmap(ucontext, mm2); + } + PDBG("%s cqid 0x%0x chp %p size %u memsize %zu, dma_addr 0x%0llx\n", + __func__, chp->cq.cqid, chp, chp->cq.size, + chp->cq.memsize, + (unsigned long long) chp->cq.dma_addr); + return &chp->ibcq; +err5: + kfree(mm2); +err4: + kfree(mm); +err3: + remove_handle(rhp, &rhp->cqidr, chp->cq.cqid); +err2: + destroy_cq(&chp->rhp->rdev, &chp->cq, + ucontext ? &ucontext->uctx : &rhp->rdev.uctx); +err1: + kfree(chp); + return ERR_PTR(ret); +} + +int c4iw_resize_cq(struct ib_cq *cq, int cqe, struct ib_udata *udata) +{ + return -ENOSYS; +} + +int c4iw_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags) +{ + struct c4iw_cq *chp; + int ret; + unsigned long flag; + + chp = to_c4iw_cq(ibcq); + spin_lock_irqsave(&chp->lock, flag); + ret = t4_arm_cq(&chp->cq, + (flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED); + spin_unlock_irqrestore(&chp->lock, flag); + if (ret && !(flags & IB_CQ_REPORT_MISSED_EVENTS)) + ret = 0; + return ret; +} diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c new file mode 100644 index 000000000000..be23b5eab13b --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/debugfs.h> + +#include <rdma/ib_verbs.h> + +#include "iw_cxgb4.h" + +#define DRV_VERSION "0.1" + +MODULE_AUTHOR("Steve Wise"); +MODULE_DESCRIPTION("Chelsio T4 RDMA Driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRV_VERSION); + +static LIST_HEAD(dev_list); +static DEFINE_MUTEX(dev_mutex); + +static struct dentry *c4iw_debugfs_root; + +struct debugfs_qp_data { + struct c4iw_dev *devp; + char *buf; + int bufsize; + int pos; +}; + +static int count_qps(int id, void *p, void *data) +{ + struct c4iw_qp *qp = p; + int *countp = data; + + if (id != qp->wq.sq.qid) + return 0; + + *countp = *countp + 1; + return 0; +} + +static int dump_qps(int id, void *p, void *data) +{ + struct c4iw_qp *qp = p; + struct debugfs_qp_data *qpd = data; + int space; + int cc; + + if (id != qp->wq.sq.qid) + return 0; + + space = qpd->bufsize - qpd->pos - 1; + if (space == 0) + return 1; + + if (qp->ep) + cc = snprintf(qpd->buf + qpd->pos, space, "qp id %u state %u " + "ep tid %u state %u %pI4:%u->%pI4:%u\n", + qp->wq.sq.qid, (int)qp->attr.state, + qp->ep->hwtid, (int)qp->ep->com.state, + &qp->ep->com.local_addr.sin_addr.s_addr, + ntohs(qp->ep->com.local_addr.sin_port), + &qp->ep->com.remote_addr.sin_addr.s_addr, + ntohs(qp->ep->com.remote_addr.sin_port)); + else + cc = snprintf(qpd->buf + qpd->pos, space, "qp id %u state %u\n", + qp->wq.sq.qid, (int)qp->attr.state); + if (cc < space) + qpd->pos += cc; + return 0; +} + +static int qp_release(struct inode *inode, struct file *file) +{ + struct debugfs_qp_data *qpd = file->private_data; + if (!qpd) { + printk(KERN_INFO "%s null qpd?\n", __func__); + return 0; + } + kfree(qpd->buf); + kfree(qpd); + return 0; +} + +static int qp_open(struct inode *inode, struct file *file) +{ + struct debugfs_qp_data *qpd; + int ret = 0; + int count = 1; + + qpd = kmalloc(sizeof *qpd, GFP_KERNEL); + if (!qpd) { + ret = -ENOMEM; + goto out; + } + qpd->devp = inode->i_private; + qpd->pos = 0; + + spin_lock_irq(&qpd->devp->lock); + idr_for_each(&qpd->devp->qpidr, count_qps, &count); + spin_unlock_irq(&qpd->devp->lock); + + qpd->bufsize = count * 128; + qpd->buf = kmalloc(qpd->bufsize, GFP_KERNEL); + if (!qpd->buf) { + ret = -ENOMEM; + goto err1; + } + + spin_lock_irq(&qpd->devp->lock); + idr_for_each(&qpd->devp->qpidr, dump_qps, qpd); + spin_unlock_irq(&qpd->devp->lock); + + qpd->buf[qpd->pos++] = 0; + file->private_data = qpd; + goto out; +err1: + kfree(qpd); +out: + return ret; +} + +static ssize_t qp_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct debugfs_qp_data *qpd = file->private_data; + loff_t pos = *ppos; + loff_t avail = qpd->pos; + + if (pos < 0) + return -EINVAL; + if (pos >= avail) + return 0; + if (count > avail - pos) + count = avail - pos; + + while (count) { + size_t len = 0; + + len = min((int)count, (int)qpd->pos - (int)pos); + if (copy_to_user(buf, qpd->buf + pos, len)) + return -EFAULT; + if (len == 0) + return -EINVAL; + + buf += len; + pos += len; + count -= len; + } + count = pos - *ppos; + *ppos = pos; + return count; +} + +static const struct file_operations qp_debugfs_fops = { + .owner = THIS_MODULE, + .open = qp_open, + .release = qp_release, + .read = qp_read, +}; + +static int setup_debugfs(struct c4iw_dev *devp) +{ + struct dentry *de; + + if (!devp->debugfs_root) + return -1; + + de = debugfs_create_file("qps", S_IWUSR, devp->debugfs_root, + (void *)devp, &qp_debugfs_fops); + if (de && de->d_inode) + de->d_inode->i_size = 4096; + return 0; +} + +void c4iw_release_dev_ucontext(struct c4iw_rdev *rdev, + struct c4iw_dev_ucontext *uctx) +{ + struct list_head *pos, *nxt; + struct c4iw_qid_list *entry; + + mutex_lock(&uctx->lock); + list_for_each_safe(pos, nxt, &uctx->qpids) { + entry = list_entry(pos, struct c4iw_qid_list, entry); + list_del_init(&entry->entry); + if (!(entry->qid & rdev->qpmask)) + c4iw_put_resource(&rdev->resource.qid_fifo, entry->qid, + &rdev->resource.qid_fifo_lock); + kfree(entry); + } + + list_for_each_safe(pos, nxt, &uctx->qpids) { + entry = list_entry(pos, struct c4iw_qid_list, entry); + list_del_init(&entry->entry); + kfree(entry); + } + mutex_unlock(&uctx->lock); +} + +void c4iw_init_dev_ucontext(struct c4iw_rdev *rdev, + struct c4iw_dev_ucontext *uctx) +{ + INIT_LIST_HEAD(&uctx->qpids); + INIT_LIST_HEAD(&uctx->cqids); + mutex_init(&uctx->lock); +} + +/* Caller takes care of locking if needed */ +static int c4iw_rdev_open(struct c4iw_rdev *rdev) +{ + int err; + + c4iw_init_dev_ucontext(rdev, &rdev->uctx); + + /* + * qpshift is the number of bits to shift the qpid left in order + * to get the correct address of the doorbell for that qp. + */ + rdev->qpshift = PAGE_SHIFT - ilog2(rdev->lldi.udb_density); + rdev->qpmask = rdev->lldi.udb_density - 1; + rdev->cqshift = PAGE_SHIFT - ilog2(rdev->lldi.ucq_density); + rdev->cqmask = rdev->lldi.ucq_density - 1; + PDBG("%s dev %s stag start 0x%0x size 0x%0x num stags %d " + "pbl start 0x%0x size 0x%0x rq start 0x%0x size 0x%0x\n", + __func__, pci_name(rdev->lldi.pdev), rdev->lldi.vr->stag.start, + rdev->lldi.vr->stag.size, c4iw_num_stags(rdev), + rdev->lldi.vr->pbl.start, + rdev->lldi.vr->pbl.size, rdev->lldi.vr->rq.start, + rdev->lldi.vr->rq.size); + PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p qpshift %lu " + "qpmask 0x%x cqshift %lu cqmask 0x%x\n", + (unsigned)pci_resource_len(rdev->lldi.pdev, 2), + (void *)pci_resource_start(rdev->lldi.pdev, 2), + rdev->lldi.db_reg, + rdev->lldi.gts_reg, + rdev->qpshift, rdev->qpmask, + rdev->cqshift, rdev->cqmask); + + if (c4iw_num_stags(rdev) == 0) { + err = -EINVAL; + goto err1; + } + + err = c4iw_init_resource(rdev, c4iw_num_stags(rdev), T4_MAX_NUM_PD); + if (err) { + printk(KERN_ERR MOD "error %d initializing resources\n", err); + goto err1; + } + err = c4iw_pblpool_create(rdev); + if (err) { + printk(KERN_ERR MOD "error %d initializing pbl pool\n", err); + goto err2; + } + err = c4iw_rqtpool_create(rdev); + if (err) { + printk(KERN_ERR MOD "error %d initializing rqt pool\n", err); + goto err3; + } + return 0; +err3: + c4iw_pblpool_destroy(rdev); +err2: + c4iw_destroy_resource(&rdev->resource); +err1: + return err; +} + +static void c4iw_rdev_close(struct c4iw_rdev *rdev) +{ + c4iw_pblpool_destroy(rdev); + c4iw_rqtpool_destroy(rdev); + c4iw_destroy_resource(&rdev->resource); +} + +static void c4iw_remove(struct c4iw_dev *dev) +{ + PDBG("%s c4iw_dev %p\n", __func__, dev); + cancel_delayed_work_sync(&dev->db_drop_task); + list_del(&dev->entry); + c4iw_unregister_device(dev); + c4iw_rdev_close(&dev->rdev); + idr_destroy(&dev->cqidr); + idr_destroy(&dev->qpidr); + idr_destroy(&dev->mmidr); + ib_dealloc_device(&dev->ibdev); +} + +static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop) +{ + struct c4iw_dev *devp; + int ret; + + devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp)); + if (!devp) { + printk(KERN_ERR MOD "Cannot allocate ib device\n"); + return NULL; + } + devp->rdev.lldi = *infop; + + mutex_lock(&dev_mutex); + + ret = c4iw_rdev_open(&devp->rdev); + if (ret) { + mutex_unlock(&dev_mutex); + printk(KERN_ERR MOD "Unable to open CXIO rdev err %d\n", ret); + ib_dealloc_device(&devp->ibdev); + return NULL; + } + + idr_init(&devp->cqidr); + idr_init(&devp->qpidr); + idr_init(&devp->mmidr); + spin_lock_init(&devp->lock); + list_add_tail(&devp->entry, &dev_list); + mutex_unlock(&dev_mutex); + + if (c4iw_register_device(devp)) { + printk(KERN_ERR MOD "Unable to register device\n"); + mutex_lock(&dev_mutex); + c4iw_remove(devp); + mutex_unlock(&dev_mutex); + } + if (c4iw_debugfs_root) { + devp->debugfs_root = debugfs_create_dir( + pci_name(devp->rdev.lldi.pdev), + c4iw_debugfs_root); + setup_debugfs(devp); + } + return devp; +} + +static void *c4iw_uld_add(const struct cxgb4_lld_info *infop) +{ + struct c4iw_dev *dev; + static int vers_printed; + int i; + + if (!vers_printed++) + printk(KERN_INFO MOD "Chelsio T4 RDMA Driver - version %s\n", + DRV_VERSION); + + dev = c4iw_alloc(infop); + if (!dev) + goto out; + + PDBG("%s found device %s nchan %u nrxq %u ntxq %u nports %u\n", + __func__, pci_name(dev->rdev.lldi.pdev), + dev->rdev.lldi.nchan, dev->rdev.lldi.nrxq, + dev->rdev.lldi.ntxq, dev->rdev.lldi.nports); + + for (i = 0; i < dev->rdev.lldi.nrxq; i++) + PDBG("rxqid[%u] %u\n", i, dev->rdev.lldi.rxq_ids[i]); + + printk(KERN_INFO MOD "Initialized device %s\n", + pci_name(dev->rdev.lldi.pdev)); +out: + return dev; +} + +static struct sk_buff *t4_pktgl_to_skb(const struct pkt_gl *gl, + unsigned int skb_len, + unsigned int pull_len) +{ + struct sk_buff *skb; + struct skb_shared_info *ssi; + + if (gl->tot_len <= 512) { + skb = alloc_skb(gl->tot_len, GFP_ATOMIC); + if (unlikely(!skb)) + goto out; + __skb_put(skb, gl->tot_len); + skb_copy_to_linear_data(skb, gl->va, gl->tot_len); + } else { + skb = alloc_skb(skb_len, GFP_ATOMIC); + if (unlikely(!skb)) + goto out; + __skb_put(skb, pull_len); + skb_copy_to_linear_data(skb, gl->va, pull_len); + + ssi = skb_shinfo(skb); + ssi->frags[0].page = gl->frags[0].page; + ssi->frags[0].page_offset = gl->frags[0].page_offset + pull_len; + ssi->frags[0].size = gl->frags[0].size - pull_len; + if (gl->nfrags > 1) + memcpy(&ssi->frags[1], &gl->frags[1], + (gl->nfrags - 1) * sizeof(skb_frag_t)); + ssi->nr_frags = gl->nfrags; + + skb->len = gl->tot_len; + skb->data_len = skb->len - pull_len; + skb->truesize += skb->data_len; + + /* Get a reference for the last page, we don't own it */ + get_page(gl->frags[gl->nfrags - 1].page); + } +out: + return skb; +} + +static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp, + const struct pkt_gl *gl) +{ + struct c4iw_dev *dev = handle; + struct sk_buff *skb; + const struct cpl_act_establish *rpl; + unsigned int opcode; + + if (gl == NULL) { + /* omit RSS and rsp_ctrl at end of descriptor */ + unsigned int len = 64 - sizeof(struct rsp_ctrl) - 8; + + skb = alloc_skb(256, GFP_ATOMIC); + if (!skb) + goto nomem; + __skb_put(skb, len); + skb_copy_to_linear_data(skb, &rsp[1], len); + } else if (gl == CXGB4_MSG_AN) { + const struct rsp_ctrl *rc = (void *)rsp; + + u32 qid = be32_to_cpu(rc->pldbuflen_qid); + c4iw_ev_handler(dev, qid); + return 0; + } else { + skb = t4_pktgl_to_skb(gl, 128, 128); + if (unlikely(!skb)) + goto nomem; + } + + rpl = cplhdr(skb); + opcode = rpl->ot.opcode; + + if (c4iw_handlers[opcode]) + c4iw_handlers[opcode](dev, skb); + else + printk(KERN_INFO "%s no handler opcode 0x%x...\n", __func__, + opcode); + + return 0; +nomem: + return -1; +} + +static int c4iw_uld_state_change(void *handle, enum cxgb4_state new_state) +{ + PDBG("%s new_state %u\n", __func__, new_state); + return 0; +} + +static struct cxgb4_uld_info c4iw_uld_info = { + .name = DRV_NAME, + .add = c4iw_uld_add, + .rx_handler = c4iw_uld_rx_handler, + .state_change = c4iw_uld_state_change, +}; + +static int __init c4iw_init_module(void) +{ + int err; + + err = c4iw_cm_init(); + if (err) + return err; + + c4iw_debugfs_root = debugfs_create_dir(DRV_NAME, NULL); + if (!c4iw_debugfs_root) + printk(KERN_WARNING MOD + "could not create debugfs entry, continuing\n"); + + cxgb4_register_uld(CXGB4_ULD_RDMA, &c4iw_uld_info); + + return 0; +} + +static void __exit c4iw_exit_module(void) +{ + struct c4iw_dev *dev, *tmp; + + cxgb4_unregister_uld(CXGB4_ULD_RDMA); + + mutex_lock(&dev_mutex); + list_for_each_entry_safe(dev, tmp, &dev_list, entry) { + c4iw_remove(dev); + } + mutex_unlock(&dev_mutex); + + c4iw_cm_term(); + debugfs_remove_recursive(c4iw_debugfs_root); +} + +module_init(c4iw_init_module); +module_exit(c4iw_exit_module); diff --git a/drivers/infiniband/hw/cxgb4/ev.c b/drivers/infiniband/hw/cxgb4/ev.c new file mode 100644 index 000000000000..491e76a0327f --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/ev.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + */ +#include <linux/slab.h> +#include <linux/mman.h> +#include <net/sock.h> + +#include "iw_cxgb4.h" + +static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp, + struct c4iw_qp *qhp, + struct t4_cqe *err_cqe, + enum ib_event_type ib_event) +{ + struct ib_event event; + struct c4iw_qp_attributes attrs; + + if ((qhp->attr.state == C4IW_QP_STATE_ERROR) || + (qhp->attr.state == C4IW_QP_STATE_TERMINATE)) { + PDBG("%s AE received after RTS - " + "qp state %d qpid 0x%x status 0x%x\n", __func__, + qhp->attr.state, qhp->wq.sq.qid, CQE_STATUS(err_cqe)); + return; + } + + printk(KERN_ERR MOD "AE qpid 0x%x opcode %d status 0x%x " + "type %d wrid.hi 0x%x wrid.lo 0x%x\n", + CQE_QPID(err_cqe), CQE_OPCODE(err_cqe), + CQE_STATUS(err_cqe), CQE_TYPE(err_cqe), + CQE_WRID_HI(err_cqe), CQE_WRID_LOW(err_cqe)); + + if (qhp->attr.state == C4IW_QP_STATE_RTS) { + attrs.next_state = C4IW_QP_STATE_TERMINATE; + c4iw_modify_qp(qhp->rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, + &attrs, 1); + } + + event.event = ib_event; + event.device = chp->ibcq.device; + if (ib_event == IB_EVENT_CQ_ERR) + event.element.cq = &chp->ibcq; + else + event.element.qp = &qhp->ibqp; + if (qhp->ibqp.event_handler) + (*qhp->ibqp.event_handler)(&event, qhp->ibqp.qp_context); + + (*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context); +} + +void c4iw_ev_dispatch(struct c4iw_dev *dev, struct t4_cqe *err_cqe) +{ + struct c4iw_cq *chp; + struct c4iw_qp *qhp; + u32 cqid; + + spin_lock(&dev->lock); + qhp = get_qhp(dev, CQE_QPID(err_cqe)); + if (!qhp) { + printk(KERN_ERR MOD "BAD AE qpid 0x%x opcode %d " + "status 0x%x type %d wrid.hi 0x%x wrid.lo 0x%x\n", + CQE_QPID(err_cqe), + CQE_OPCODE(err_cqe), CQE_STATUS(err_cqe), + CQE_TYPE(err_cqe), CQE_WRID_HI(err_cqe), + CQE_WRID_LOW(err_cqe)); + spin_unlock(&dev->lock); + goto out; + } + + if (SQ_TYPE(err_cqe)) + cqid = qhp->attr.scq; + else + cqid = qhp->attr.rcq; + chp = get_chp(dev, cqid); + if (!chp) { + printk(KERN_ERR MOD "BAD AE cqid 0x%x qpid 0x%x opcode %d " + "status 0x%x type %d wrid.hi 0x%x wrid.lo 0x%x\n", + cqid, CQE_QPID(err_cqe), + CQE_OPCODE(err_cqe), CQE_STATUS(err_cqe), + CQE_TYPE(err_cqe), CQE_WRID_HI(err_cqe), + CQE_WRID_LOW(err_cqe)); + spin_unlock(&dev->lock); + goto out; + } + + c4iw_qp_add_ref(&qhp->ibqp); + atomic_inc(&chp->refcnt); + spin_unlock(&dev->lock); + + /* Bad incoming write */ + if (RQ_TYPE(err_cqe) && + (CQE_OPCODE(err_cqe) == FW_RI_RDMA_WRITE)) { + post_qp_event(dev, chp, qhp, err_cqe, IB_EVENT_QP_REQ_ERR); + goto done; + } + + switch (CQE_STATUS(err_cqe)) { + + /* Completion Events */ + case T4_ERR_SUCCESS: + printk(KERN_ERR MOD "AE with status 0!\n"); + break; + + case T4_ERR_STAG: + case T4_ERR_PDID: + case T4_ERR_QPID: + case T4_ERR_ACCESS: + case T4_ERR_WRAP: + case T4_ERR_BOUND: + case T4_ERR_INVALIDATE_SHARED_MR: + case T4_ERR_INVALIDATE_MR_WITH_MW_BOUND: + post_qp_event(dev, chp, qhp, err_cqe, IB_EVENT_QP_ACCESS_ERR); + break; + + /* Device Fatal Errors */ + case T4_ERR_ECC: + case T4_ERR_ECC_PSTAG: + case T4_ERR_INTERNAL_ERR: + post_qp_event(dev, chp, qhp, err_cqe, IB_EVENT_DEVICE_FATAL); + break; + + /* QP Fatal Errors */ + case T4_ERR_OUT_OF_RQE: + case T4_ERR_PBL_ADDR_BOUND: + case T4_ERR_CRC: + case T4_ERR_MARKER: + case T4_ERR_PDU_LEN_ERR: + case T4_ERR_DDP_VERSION: + case T4_ERR_RDMA_VERSION: + case T4_ERR_OPCODE: + case T4_ERR_DDP_QUEUE_NUM: + case T4_ERR_MSN: + case T4_ERR_TBIT: + case T4_ERR_MO: + case T4_ERR_MSN_GAP: + case T4_ERR_MSN_RANGE: + case T4_ERR_RQE_ADDR_BOUND: + case T4_ERR_IRD_OVERFLOW: + post_qp_event(dev, chp, qhp, err_cqe, IB_EVENT_QP_FATAL); + break; + + default: + printk(KERN_ERR MOD "Unknown T4 status 0x%x QPID 0x%x\n", + CQE_STATUS(err_cqe), qhp->wq.sq.qid); + post_qp_event(dev, chp, qhp, err_cqe, IB_EVENT_QP_FATAL); + break; + } +done: + if (atomic_dec_and_test(&chp->refcnt)) + wake_up(&chp->wait); + c4iw_qp_rem_ref(&qhp->ibqp); +out: + return; +} + +int c4iw_ev_handler(struct c4iw_dev *dev, u32 qid) +{ + struct c4iw_cq *chp; + + chp = get_chp(dev, qid); + if (chp) + (*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context); + else + PDBG("%s unknown cqid 0x%x\n", __func__, qid); + return 0; +} diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h new file mode 100644 index 000000000000..a6269981e815 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -0,0 +1,745 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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 __IW_CXGB4_H__ +#define __IW_CXGB4_H__ + +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/idr.h> +#include <linux/workqueue.h> +#include <linux/netdevice.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/inet.h> +#include <linux/wait.h> +#include <linux/kref.h> +#include <linux/timer.h> +#include <linux/io.h> +#include <linux/kfifo.h> + +#include <asm/byteorder.h> + +#include <net/net_namespace.h> + +#include <rdma/ib_verbs.h> +#include <rdma/iw_cm.h> + +#include "cxgb4.h" +#include "cxgb4_uld.h" +#include "l2t.h" +#include "user.h" + +#define DRV_NAME "iw_cxgb4" +#define MOD DRV_NAME ":" + +extern int c4iw_debug; +#define PDBG(fmt, args...) \ +do { \ + if (c4iw_debug) \ + printk(MOD fmt, ## args); \ +} while (0) + +#include "t4.h" + +#define PBL_OFF(rdev_p, a) ((a) - (rdev_p)->lldi.vr->pbl.start) +#define RQT_OFF(rdev_p, a) ((a) - (rdev_p)->lldi.vr->rq.start) + +static inline void *cplhdr(struct sk_buff *skb) +{ + return skb->data; +} + +#define C4IW_WR_TO (10*HZ) + +struct c4iw_wr_wait { + wait_queue_head_t wait; + int done; + int ret; +}; + +static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp) +{ + wr_waitp->ret = 0; + wr_waitp->done = 0; + init_waitqueue_head(&wr_waitp->wait); +} + +struct c4iw_resource { + struct kfifo tpt_fifo; + spinlock_t tpt_fifo_lock; + struct kfifo qid_fifo; + spinlock_t qid_fifo_lock; + struct kfifo pdid_fifo; + spinlock_t pdid_fifo_lock; +}; + +struct c4iw_qid_list { + struct list_head entry; + u32 qid; +}; + +struct c4iw_dev_ucontext { + struct list_head qpids; + struct list_head cqids; + struct mutex lock; +}; + +enum c4iw_rdev_flags { + T4_FATAL_ERROR = (1<<0), +}; + +struct c4iw_rdev { + struct c4iw_resource resource; + unsigned long qpshift; + u32 qpmask; + unsigned long cqshift; + u32 cqmask; + struct c4iw_dev_ucontext uctx; + struct gen_pool *pbl_pool; + struct gen_pool *rqt_pool; + u32 flags; + struct cxgb4_lld_info lldi; +}; + +static inline int c4iw_fatal_error(struct c4iw_rdev *rdev) +{ + return rdev->flags & T4_FATAL_ERROR; +} + +static inline int c4iw_num_stags(struct c4iw_rdev *rdev) +{ + return min((int)T4_MAX_NUM_STAG, (int)(rdev->lldi.vr->stag.size >> 5)); +} + +struct c4iw_dev { + struct ib_device ibdev; + struct c4iw_rdev rdev; + u32 device_cap_flags; + struct idr cqidr; + struct idr qpidr; + struct idr mmidr; + spinlock_t lock; + struct list_head entry; + struct delayed_work db_drop_task; + struct dentry *debugfs_root; +}; + +static inline struct c4iw_dev *to_c4iw_dev(struct ib_device *ibdev) +{ + return container_of(ibdev, struct c4iw_dev, ibdev); +} + +static inline struct c4iw_dev *rdev_to_c4iw_dev(struct c4iw_rdev *rdev) +{ + return container_of(rdev, struct c4iw_dev, rdev); +} + +static inline struct c4iw_cq *get_chp(struct c4iw_dev *rhp, u32 cqid) +{ + return idr_find(&rhp->cqidr, cqid); +} + +static inline struct c4iw_qp *get_qhp(struct c4iw_dev *rhp, u32 qpid) +{ + return idr_find(&rhp->qpidr, qpid); +} + +static inline struct c4iw_mr *get_mhp(struct c4iw_dev *rhp, u32 mmid) +{ + return idr_find(&rhp->mmidr, mmid); +} + +static inline int insert_handle(struct c4iw_dev *rhp, struct idr *idr, + void *handle, u32 id) +{ + int ret; + int newid; + + do { + if (!idr_pre_get(idr, GFP_KERNEL)) + return -ENOMEM; + spin_lock_irq(&rhp->lock); + ret = idr_get_new_above(idr, handle, id, &newid); + BUG_ON(newid != id); + spin_unlock_irq(&rhp->lock); + } while (ret == -EAGAIN); + + return ret; +} + +static inline void remove_handle(struct c4iw_dev *rhp, struct idr *idr, u32 id) +{ + spin_lock_irq(&rhp->lock); + idr_remove(idr, id); + spin_unlock_irq(&rhp->lock); +} + +struct c4iw_pd { + struct ib_pd ibpd; + u32 pdid; + struct c4iw_dev *rhp; +}; + +static inline struct c4iw_pd *to_c4iw_pd(struct ib_pd *ibpd) +{ + return container_of(ibpd, struct c4iw_pd, ibpd); +} + +struct tpt_attributes { + u64 len; + u64 va_fbo; + enum fw_ri_mem_perms perms; + u32 stag; + u32 pdid; + u32 qpid; + u32 pbl_addr; + u32 pbl_size; + u32 state:1; + u32 type:2; + u32 rsvd:1; + u32 remote_invaliate_disable:1; + u32 zbva:1; + u32 mw_bind_enable:1; + u32 page_size:5; +}; + +struct c4iw_mr { + struct ib_mr ibmr; + struct ib_umem *umem; + struct c4iw_dev *rhp; + u64 kva; + struct tpt_attributes attr; +}; + +static inline struct c4iw_mr *to_c4iw_mr(struct ib_mr *ibmr) +{ + return container_of(ibmr, struct c4iw_mr, ibmr); +} + +struct c4iw_mw { + struct ib_mw ibmw; + struct c4iw_dev *rhp; + u64 kva; + struct tpt_attributes attr; +}; + +static inline struct c4iw_mw *to_c4iw_mw(struct ib_mw *ibmw) +{ + return container_of(ibmw, struct c4iw_mw, ibmw); +} + +struct c4iw_fr_page_list { + struct ib_fast_reg_page_list ibpl; + DECLARE_PCI_UNMAP_ADDR(mapping); + dma_addr_t dma_addr; + struct c4iw_dev *dev; + int size; +}; + +static inline struct c4iw_fr_page_list *to_c4iw_fr_page_list( + struct ib_fast_reg_page_list *ibpl) +{ + return container_of(ibpl, struct c4iw_fr_page_list, ibpl); +} + +struct c4iw_cq { + struct ib_cq ibcq; + struct c4iw_dev *rhp; + struct t4_cq cq; + spinlock_t lock; + atomic_t refcnt; + wait_queue_head_t wait; +}; + +static inline struct c4iw_cq *to_c4iw_cq(struct ib_cq *ibcq) +{ + return container_of(ibcq, struct c4iw_cq, ibcq); +} + +struct c4iw_mpa_attributes { + u8 initiator; + u8 recv_marker_enabled; + u8 xmit_marker_enabled; + u8 crc_enabled; + u8 version; + u8 p2p_type; +}; + +struct c4iw_qp_attributes { + u32 scq; + u32 rcq; + u32 sq_num_entries; + u32 rq_num_entries; + u32 sq_max_sges; + u32 sq_max_sges_rdma_write; + u32 rq_max_sges; + u32 state; + u8 enable_rdma_read; + u8 enable_rdma_write; + u8 enable_bind; + u8 enable_mmid0_fastreg; + u32 max_ord; + u32 max_ird; + u32 pd; + u32 next_state; + char terminate_buffer[52]; + u32 terminate_msg_len; + u8 is_terminate_local; + struct c4iw_mpa_attributes mpa_attr; + struct c4iw_ep *llp_stream_handle; +}; + +struct c4iw_qp { + struct ib_qp ibqp; + struct c4iw_dev *rhp; + struct c4iw_ep *ep; + struct c4iw_qp_attributes attr; + struct t4_wq wq; + spinlock_t lock; + atomic_t refcnt; + wait_queue_head_t wait; + struct timer_list timer; +}; + +static inline struct c4iw_qp *to_c4iw_qp(struct ib_qp *ibqp) +{ + return container_of(ibqp, struct c4iw_qp, ibqp); +} + +struct c4iw_ucontext { + struct ib_ucontext ibucontext; + struct c4iw_dev_ucontext uctx; + u32 key; + spinlock_t mmap_lock; + struct list_head mmaps; +}; + +static inline struct c4iw_ucontext *to_c4iw_ucontext(struct ib_ucontext *c) +{ + return container_of(c, struct c4iw_ucontext, ibucontext); +} + +struct c4iw_mm_entry { + struct list_head entry; + u64 addr; + u32 key; + unsigned len; +}; + +static inline struct c4iw_mm_entry *remove_mmap(struct c4iw_ucontext *ucontext, + u32 key, unsigned len) +{ + struct list_head *pos, *nxt; + struct c4iw_mm_entry *mm; + + spin_lock(&ucontext->mmap_lock); + list_for_each_safe(pos, nxt, &ucontext->mmaps) { + + mm = list_entry(pos, struct c4iw_mm_entry, entry); + if (mm->key == key && mm->len == len) { + list_del_init(&mm->entry); + spin_unlock(&ucontext->mmap_lock); + PDBG("%s key 0x%x addr 0x%llx len %d\n", __func__, + key, (unsigned long long) mm->addr, mm->len); + return mm; + } + } + spin_unlock(&ucontext->mmap_lock); + return NULL; +} + +static inline void insert_mmap(struct c4iw_ucontext *ucontext, + struct c4iw_mm_entry *mm) +{ + spin_lock(&ucontext->mmap_lock); + PDBG("%s key 0x%x addr 0x%llx len %d\n", __func__, + mm->key, (unsigned long long) mm->addr, mm->len); + list_add_tail(&mm->entry, &ucontext->mmaps); + spin_unlock(&ucontext->mmap_lock); +} + +enum c4iw_qp_attr_mask { + C4IW_QP_ATTR_NEXT_STATE = 1 << 0, + C4IW_QP_ATTR_ENABLE_RDMA_READ = 1 << 7, + C4IW_QP_ATTR_ENABLE_RDMA_WRITE = 1 << 8, + C4IW_QP_ATTR_ENABLE_RDMA_BIND = 1 << 9, + C4IW_QP_ATTR_MAX_ORD = 1 << 11, + C4IW_QP_ATTR_MAX_IRD = 1 << 12, + C4IW_QP_ATTR_LLP_STREAM_HANDLE = 1 << 22, + C4IW_QP_ATTR_STREAM_MSG_BUFFER = 1 << 23, + C4IW_QP_ATTR_MPA_ATTR = 1 << 24, + C4IW_QP_ATTR_QP_CONTEXT_ACTIVATE = 1 << 25, + C4IW_QP_ATTR_VALID_MODIFY = (C4IW_QP_ATTR_ENABLE_RDMA_READ | + C4IW_QP_ATTR_ENABLE_RDMA_WRITE | + C4IW_QP_ATTR_MAX_ORD | + C4IW_QP_ATTR_MAX_IRD | + C4IW_QP_ATTR_LLP_STREAM_HANDLE | + C4IW_QP_ATTR_STREAM_MSG_BUFFER | + C4IW_QP_ATTR_MPA_ATTR | + C4IW_QP_ATTR_QP_CONTEXT_ACTIVATE) +}; + +int c4iw_modify_qp(struct c4iw_dev *rhp, + struct c4iw_qp *qhp, + enum c4iw_qp_attr_mask mask, + struct c4iw_qp_attributes *attrs, + int internal); + +enum c4iw_qp_state { + C4IW_QP_STATE_IDLE, + C4IW_QP_STATE_RTS, + C4IW_QP_STATE_ERROR, + C4IW_QP_STATE_TERMINATE, + C4IW_QP_STATE_CLOSING, + C4IW_QP_STATE_TOT +}; + +static inline int c4iw_convert_state(enum ib_qp_state ib_state) +{ + switch (ib_state) { + case IB_QPS_RESET: + case IB_QPS_INIT: + return C4IW_QP_STATE_IDLE; + case IB_QPS_RTS: + return C4IW_QP_STATE_RTS; + case IB_QPS_SQD: + return C4IW_QP_STATE_CLOSING; + case IB_QPS_SQE: + return C4IW_QP_STATE_TERMINATE; + case IB_QPS_ERR: + return C4IW_QP_STATE_ERROR; + default: + return -1; + } +} + +static inline u32 c4iw_ib_to_tpt_access(int a) +{ + return (a & IB_ACCESS_REMOTE_WRITE ? FW_RI_MEM_ACCESS_REM_WRITE : 0) | + (a & IB_ACCESS_REMOTE_READ ? FW_RI_MEM_ACCESS_REM_READ : 0) | + (a & IB_ACCESS_LOCAL_WRITE ? FW_RI_MEM_ACCESS_LOCAL_WRITE : 0) | + FW_RI_MEM_ACCESS_LOCAL_READ; +} + +static inline u32 c4iw_ib_to_tpt_bind_access(int acc) +{ + return (acc & IB_ACCESS_REMOTE_WRITE ? FW_RI_MEM_ACCESS_REM_WRITE : 0) | + (acc & IB_ACCESS_REMOTE_READ ? FW_RI_MEM_ACCESS_REM_READ : 0); +} + +enum c4iw_mmid_state { + C4IW_STAG_STATE_VALID, + C4IW_STAG_STATE_INVALID +}; + +#define C4IW_NODE_DESC "cxgb4 Chelsio Communications" + +#define MPA_KEY_REQ "MPA ID Req Frame" +#define MPA_KEY_REP "MPA ID Rep Frame" + +#define MPA_MAX_PRIVATE_DATA 256 +#define MPA_REJECT 0x20 +#define MPA_CRC 0x40 +#define MPA_MARKERS 0x80 +#define MPA_FLAGS_MASK 0xE0 + +#define c4iw_put_ep(ep) { \ + PDBG("put_ep (via %s:%u) ep %p refcnt %d\n", __func__, __LINE__, \ + ep, atomic_read(&((ep)->kref.refcount))); \ + WARN_ON(atomic_read(&((ep)->kref.refcount)) < 1); \ + kref_put(&((ep)->kref), _c4iw_free_ep); \ +} + +#define c4iw_get_ep(ep) { \ + PDBG("get_ep (via %s:%u) ep %p, refcnt %d\n", __func__, __LINE__, \ + ep, atomic_read(&((ep)->kref.refcount))); \ + kref_get(&((ep)->kref)); \ +} +void _c4iw_free_ep(struct kref *kref); + +struct mpa_message { + u8 key[16]; + u8 flags; + u8 revision; + __be16 private_data_size; + u8 private_data[0]; +}; + +struct terminate_message { + u8 layer_etype; + u8 ecode; + __be16 hdrct_rsvd; + u8 len_hdrs[0]; +}; + +#define TERM_MAX_LENGTH (sizeof(struct terminate_message) + 2 + 18 + 28) + +enum c4iw_layers_types { + LAYER_RDMAP = 0x00, + LAYER_DDP = 0x10, + LAYER_MPA = 0x20, + RDMAP_LOCAL_CATA = 0x00, + RDMAP_REMOTE_PROT = 0x01, + RDMAP_REMOTE_OP = 0x02, + DDP_LOCAL_CATA = 0x00, + DDP_TAGGED_ERR = 0x01, + DDP_UNTAGGED_ERR = 0x02, + DDP_LLP = 0x03 +}; + +enum c4iw_rdma_ecodes { + RDMAP_INV_STAG = 0x00, + RDMAP_BASE_BOUNDS = 0x01, + RDMAP_ACC_VIOL = 0x02, + RDMAP_STAG_NOT_ASSOC = 0x03, + RDMAP_TO_WRAP = 0x04, + RDMAP_INV_VERS = 0x05, + RDMAP_INV_OPCODE = 0x06, + RDMAP_STREAM_CATA = 0x07, + RDMAP_GLOBAL_CATA = 0x08, + RDMAP_CANT_INV_STAG = 0x09, + RDMAP_UNSPECIFIED = 0xff +}; + +enum c4iw_ddp_ecodes { + DDPT_INV_STAG = 0x00, + DDPT_BASE_BOUNDS = 0x01, + DDPT_STAG_NOT_ASSOC = 0x02, + DDPT_TO_WRAP = 0x03, + DDPT_INV_VERS = 0x04, + DDPU_INV_QN = 0x01, + DDPU_INV_MSN_NOBUF = 0x02, + DDPU_INV_MSN_RANGE = 0x03, + DDPU_INV_MO = 0x04, + DDPU_MSG_TOOBIG = 0x05, + DDPU_INV_VERS = 0x06 +}; + +enum c4iw_mpa_ecodes { + MPA_CRC_ERR = 0x02, + MPA_MARKER_ERR = 0x03 +}; + +enum c4iw_ep_state { + IDLE = 0, + LISTEN, + CONNECTING, + MPA_REQ_WAIT, + MPA_REQ_SENT, + MPA_REQ_RCVD, + MPA_REP_SENT, + FPDU_MODE, + ABORTING, + CLOSING, + MORIBUND, + DEAD, +}; + +enum c4iw_ep_flags { + PEER_ABORT_IN_PROGRESS = 0, + ABORT_REQ_IN_PROGRESS = 1, + RELEASE_RESOURCES = 2, + CLOSE_SENT = 3, +}; + +struct c4iw_ep_common { + struct iw_cm_id *cm_id; + struct c4iw_qp *qp; + struct c4iw_dev *dev; + enum c4iw_ep_state state; + struct kref kref; + spinlock_t lock; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + wait_queue_head_t waitq; + int rpl_done; + int rpl_err; + unsigned long flags; +}; + +struct c4iw_listen_ep { + struct c4iw_ep_common com; + unsigned int stid; + int backlog; +}; + +struct c4iw_ep { + struct c4iw_ep_common com; + struct c4iw_ep *parent_ep; + struct timer_list timer; + struct list_head entry; + unsigned int atid; + u32 hwtid; + u32 snd_seq; + u32 rcv_seq; + struct l2t_entry *l2t; + struct dst_entry *dst; + struct sk_buff *mpa_skb; + struct c4iw_mpa_attributes mpa_attr; + u8 mpa_pkt[sizeof(struct mpa_message) + MPA_MAX_PRIVATE_DATA]; + unsigned int mpa_pkt_len; + u32 ird; + u32 ord; + u32 smac_idx; + u32 tx_chan; + u32 mtu; + u16 mss; + u16 emss; + u16 plen; + u16 rss_qid; + u16 txq_idx; + u8 tos; +}; + +static inline struct c4iw_ep *to_ep(struct iw_cm_id *cm_id) +{ + return cm_id->provider_data; +} + +static inline struct c4iw_listen_ep *to_listen_ep(struct iw_cm_id *cm_id) +{ + return cm_id->provider_data; +} + +static inline int compute_wscale(int win) +{ + int wscale = 0; + + while (wscale < 14 && (65535<<wscale) < win) + wscale++; + return wscale; +} + +typedef int (*c4iw_handler_func)(struct c4iw_dev *dev, struct sk_buff *skb); + +int c4iw_ep_redirect(void *ctx, struct dst_entry *old, struct dst_entry *new, + struct l2t_entry *l2t); +void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qpid, + struct c4iw_dev_ucontext *uctx); +u32 c4iw_get_resource(struct kfifo *fifo, spinlock_t *lock); +void c4iw_put_resource(struct kfifo *fifo, u32 entry, spinlock_t *lock); +int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, u32 nr_pdid); +int c4iw_init_ctrl_qp(struct c4iw_rdev *rdev); +int c4iw_pblpool_create(struct c4iw_rdev *rdev); +int c4iw_rqtpool_create(struct c4iw_rdev *rdev); +void c4iw_pblpool_destroy(struct c4iw_rdev *rdev); +void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev); +void c4iw_destroy_resource(struct c4iw_resource *rscp); +int c4iw_destroy_ctrl_qp(struct c4iw_rdev *rdev); +int c4iw_register_device(struct c4iw_dev *dev); +void c4iw_unregister_device(struct c4iw_dev *dev); +int __init c4iw_cm_init(void); +void __exit c4iw_cm_term(void); +void c4iw_release_dev_ucontext(struct c4iw_rdev *rdev, + struct c4iw_dev_ucontext *uctx); +void c4iw_init_dev_ucontext(struct c4iw_rdev *rdev, + struct c4iw_dev_ucontext *uctx); +int c4iw_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc); +int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, + struct ib_send_wr **bad_wr); +int c4iw_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr); +int c4iw_bind_mw(struct ib_qp *qp, struct ib_mw *mw, + struct ib_mw_bind *mw_bind); +int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param); +int c4iw_create_listen(struct iw_cm_id *cm_id, int backlog); +int c4iw_destroy_listen(struct iw_cm_id *cm_id); +int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param); +int c4iw_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len); +void c4iw_qp_add_ref(struct ib_qp *qp); +void c4iw_qp_rem_ref(struct ib_qp *qp); +void c4iw_free_fastreg_pbl(struct ib_fast_reg_page_list *page_list); +struct ib_fast_reg_page_list *c4iw_alloc_fastreg_pbl( + struct ib_device *device, + int page_list_len); +struct ib_mr *c4iw_alloc_fast_reg_mr(struct ib_pd *pd, int pbl_depth); +int c4iw_dealloc_mw(struct ib_mw *mw); +struct ib_mw *c4iw_alloc_mw(struct ib_pd *pd); +struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, + u64 length, u64 virt, int acc, + struct ib_udata *udata); +struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc); +struct ib_mr *c4iw_register_phys_mem(struct ib_pd *pd, + struct ib_phys_buf *buffer_list, + int num_phys_buf, + int acc, + u64 *iova_start); +int c4iw_reregister_phys_mem(struct ib_mr *mr, + int mr_rereg_mask, + struct ib_pd *pd, + struct ib_phys_buf *buffer_list, + int num_phys_buf, + int acc, u64 *iova_start); +int c4iw_dereg_mr(struct ib_mr *ib_mr); +int c4iw_destroy_cq(struct ib_cq *ib_cq); +struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries, + int vector, + struct ib_ucontext *ib_context, + struct ib_udata *udata); +int c4iw_resize_cq(struct ib_cq *cq, int cqe, struct ib_udata *udata); +int c4iw_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags); +int c4iw_destroy_qp(struct ib_qp *ib_qp); +struct ib_qp *c4iw_create_qp(struct ib_pd *pd, + struct ib_qp_init_attr *attrs, + struct ib_udata *udata); +int c4iw_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask, struct ib_udata *udata); +struct ib_qp *c4iw_get_qp(struct ib_device *dev, int qpn); +u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size); +void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size); +u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size); +void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size); +int c4iw_ofld_send(struct c4iw_rdev *rdev, struct sk_buff *skb); +void c4iw_flush_hw_cq(struct t4_cq *cq); +void c4iw_count_rcqes(struct t4_cq *cq, struct t4_wq *wq, int *count); +void c4iw_count_scqes(struct t4_cq *cq, struct t4_wq *wq, int *count); +int c4iw_ep_disconnect(struct c4iw_ep *ep, int abrupt, gfp_t gfp); +int c4iw_flush_rq(struct t4_wq *wq, struct t4_cq *cq, int count); +int c4iw_flush_sq(struct t4_wq *wq, struct t4_cq *cq, int count); +int c4iw_ev_handler(struct c4iw_dev *rnicp, u32 qid); +u16 c4iw_rqes_posted(struct c4iw_qp *qhp); +int c4iw_post_zb_read(struct c4iw_qp *qhp); +int c4iw_post_terminate(struct c4iw_qp *qhp, struct t4_cqe *err_cqe); +u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx); +void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid, + struct c4iw_dev_ucontext *uctx); +u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx); +void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid, + struct c4iw_dev_ucontext *uctx); +void c4iw_ev_dispatch(struct c4iw_dev *dev, struct t4_cqe *err_cqe); + +extern struct cxgb4_client t4c_client; +extern c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS]; +extern int c4iw_max_read_depth; + +#endif diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c new file mode 100644 index 000000000000..e54ff6d25691 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + */ + +#include <rdma/ib_umem.h> +#include <asm/atomic.h> + +#include "iw_cxgb4.h" + +#define T4_ULPTX_MIN_IO 32 +#define C4IW_MAX_INLINE_SIZE 96 + +static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len, + void *data) +{ + struct sk_buff *skb; + struct ulp_mem_io *req; + struct ulptx_idata *sc; + u8 wr_len, *to_dp, *from_dp; + int copy_len, num_wqe, i, ret = 0; + struct c4iw_wr_wait wr_wait; + + addr &= 0x7FFFFFF; + PDBG("%s addr 0x%x len %u\n", __func__, addr, len); + num_wqe = DIV_ROUND_UP(len, C4IW_MAX_INLINE_SIZE); + c4iw_init_wr_wait(&wr_wait); + for (i = 0; i < num_wqe; i++) { + + copy_len = len > C4IW_MAX_INLINE_SIZE ? C4IW_MAX_INLINE_SIZE : + len; + wr_len = roundup(sizeof *req + sizeof *sc + + roundup(copy_len, T4_ULPTX_MIN_IO), 16); + + skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL); + if (!skb) + return -ENOMEM; + set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); + + req = (struct ulp_mem_io *)__skb_put(skb, wr_len); + memset(req, 0, wr_len); + INIT_ULPTX_WR(req, wr_len, 0, 0); + + if (i == (num_wqe-1)) { + req->wr.wr_hi = cpu_to_be32(FW_WR_OP(FW_ULPTX_WR) | + FW_WR_COMPL(1)); + req->wr.wr_lo = (__force __be64)&wr_wait; + } else + req->wr.wr_hi = cpu_to_be32(FW_WR_OP(FW_ULPTX_WR)); + req->wr.wr_mid = cpu_to_be32( + FW_WR_LEN16(DIV_ROUND_UP(wr_len, 16))); + + req->cmd = cpu_to_be32(ULPTX_CMD(ULP_TX_MEM_WRITE) | (1<<23)); + req->dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN( + DIV_ROUND_UP(copy_len, T4_ULPTX_MIN_IO))); + req->len16 = cpu_to_be32(DIV_ROUND_UP(wr_len-sizeof(req->wr), + 16)); + req->lock_addr = cpu_to_be32(ULP_MEMIO_ADDR(addr + i * 3)); + + sc = (struct ulptx_idata *)(req + 1); + sc->cmd_more = cpu_to_be32(ULPTX_CMD(ULP_TX_SC_IMM)); + sc->len = cpu_to_be32(roundup(copy_len, T4_ULPTX_MIN_IO)); + + to_dp = (u8 *)(sc + 1); + from_dp = (u8 *)data + i * C4IW_MAX_INLINE_SIZE; + if (data) + memcpy(to_dp, from_dp, copy_len); + else + memset(to_dp, 0, copy_len); + if (copy_len % T4_ULPTX_MIN_IO) + memset(to_dp + copy_len, 0, T4_ULPTX_MIN_IO - + (copy_len % T4_ULPTX_MIN_IO)); + ret = c4iw_ofld_send(rdev, skb); + if (ret) + return ret; + len -= C4IW_MAX_INLINE_SIZE; + } + + wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); + if (!wr_wait.done) { + printk(KERN_ERR MOD "Device %s not responding!\n", + pci_name(rdev->lldi.pdev)); + rdev->flags = T4_FATAL_ERROR; + ret = -EIO; + } else + ret = wr_wait.ret; + return ret; +} + +/* + * Build and write a TPT entry. + * IN: stag key, pdid, perm, bind_enabled, zbva, to, len, page_size, + * pbl_size and pbl_addr + * OUT: stag index + */ +static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, + u32 *stag, u8 stag_state, u32 pdid, + enum fw_ri_stag_type type, enum fw_ri_mem_perms perm, + int bind_enabled, u32 zbva, u64 to, + u64 len, u8 page_size, u32 pbl_size, u32 pbl_addr) +{ + int err; + struct fw_ri_tpte tpt; + u32 stag_idx; + static atomic_t key; + + if (c4iw_fatal_error(rdev)) + return -EIO; + + stag_state = stag_state > 0; + stag_idx = (*stag) >> 8; + + if ((!reset_tpt_entry) && (*stag == T4_STAG_UNSET)) { + stag_idx = c4iw_get_resource(&rdev->resource.tpt_fifo, + &rdev->resource.tpt_fifo_lock); + if (!stag_idx) + return -ENOMEM; + *stag = (stag_idx << 8) | (atomic_inc_return(&key) & 0xff); + } + PDBG("%s stag_state 0x%0x type 0x%0x pdid 0x%0x, stag_idx 0x%x\n", + __func__, stag_state, type, pdid, stag_idx); + + /* write TPT entry */ + if (reset_tpt_entry) + memset(&tpt, 0, sizeof(tpt)); + else { + tpt.valid_to_pdid = cpu_to_be32(F_FW_RI_TPTE_VALID | + V_FW_RI_TPTE_STAGKEY((*stag & M_FW_RI_TPTE_STAGKEY)) | + V_FW_RI_TPTE_STAGSTATE(stag_state) | + V_FW_RI_TPTE_STAGTYPE(type) | V_FW_RI_TPTE_PDID(pdid)); + tpt.locread_to_qpid = cpu_to_be32(V_FW_RI_TPTE_PERM(perm) | + (bind_enabled ? F_FW_RI_TPTE_MWBINDEN : 0) | + V_FW_RI_TPTE_ADDRTYPE((zbva ? FW_RI_ZERO_BASED_TO : + FW_RI_VA_BASED_TO))| + V_FW_RI_TPTE_PS(page_size)); + tpt.nosnoop_pbladdr = !pbl_size ? 0 : cpu_to_be32( + V_FW_RI_TPTE_PBLADDR(PBL_OFF(rdev, pbl_addr)>>3)); + tpt.len_lo = cpu_to_be32((u32)(len & 0xffffffffUL)); + tpt.va_hi = cpu_to_be32((u32)(to >> 32)); + tpt.va_lo_fbo = cpu_to_be32((u32)(to & 0xffffffffUL)); + tpt.dca_mwbcnt_pstag = cpu_to_be32(0); + tpt.len_hi = cpu_to_be32((u32)(len >> 32)); + } + err = write_adapter_mem(rdev, stag_idx + + (rdev->lldi.vr->stag.start >> 5), + sizeof(tpt), &tpt); + + if (reset_tpt_entry) + c4iw_put_resource(&rdev->resource.tpt_fifo, stag_idx, + &rdev->resource.tpt_fifo_lock); + return err; +} + +static int write_pbl(struct c4iw_rdev *rdev, __be64 *pbl, + u32 pbl_addr, u32 pbl_size) +{ + int err; + + PDBG("%s *pdb_addr 0x%x, pbl_base 0x%x, pbl_size %d\n", + __func__, pbl_addr, rdev->lldi.vr->pbl.start, + pbl_size); + + err = write_adapter_mem(rdev, pbl_addr >> 5, pbl_size << 3, pbl); + return err; +} + +static int dereg_mem(struct c4iw_rdev *rdev, u32 stag, u32 pbl_size, + u32 pbl_addr) +{ + return write_tpt_entry(rdev, 1, &stag, 0, 0, 0, 0, 0, 0, 0UL, 0, 0, + pbl_size, pbl_addr); +} + +static int allocate_window(struct c4iw_rdev *rdev, u32 * stag, u32 pdid) +{ + *stag = T4_STAG_UNSET; + return write_tpt_entry(rdev, 0, stag, 0, pdid, FW_RI_STAG_MW, 0, 0, 0, + 0UL, 0, 0, 0, 0); +} + +static int deallocate_window(struct c4iw_rdev *rdev, u32 stag) +{ + return write_tpt_entry(rdev, 1, &stag, 0, 0, 0, 0, 0, 0, 0UL, 0, 0, 0, + 0); +} + +static int allocate_stag(struct c4iw_rdev *rdev, u32 *stag, u32 pdid, + u32 pbl_size, u32 pbl_addr) +{ + *stag = T4_STAG_UNSET; + return write_tpt_entry(rdev, 0, stag, 0, pdid, FW_RI_STAG_NSMR, 0, 0, 0, + 0UL, 0, 0, pbl_size, pbl_addr); +} + +static int finish_mem_reg(struct c4iw_mr *mhp, u32 stag) +{ + u32 mmid; + + mhp->attr.state = 1; + mhp->attr.stag = stag; + mmid = stag >> 8; + mhp->ibmr.rkey = mhp->ibmr.lkey = stag; + PDBG("%s mmid 0x%x mhp %p\n", __func__, mmid, mhp); + return insert_handle(mhp->rhp, &mhp->rhp->mmidr, mhp, mmid); +} + +static int register_mem(struct c4iw_dev *rhp, struct c4iw_pd *php, + struct c4iw_mr *mhp, int shift) +{ + u32 stag = T4_STAG_UNSET; + int ret; + + ret = write_tpt_entry(&rhp->rdev, 0, &stag, 1, mhp->attr.pdid, + FW_RI_STAG_NSMR, mhp->attr.perms, + mhp->attr.mw_bind_enable, mhp->attr.zbva, + mhp->attr.va_fbo, mhp->attr.len, shift - 12, + mhp->attr.pbl_size, mhp->attr.pbl_addr); + if (ret) + return ret; + + ret = finish_mem_reg(mhp, stag); + if (ret) + dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size, + mhp->attr.pbl_addr); + return ret; +} + +static int reregister_mem(struct c4iw_dev *rhp, struct c4iw_pd *php, + struct c4iw_mr *mhp, int shift, int npages) +{ + u32 stag; + int ret; + + if (npages > mhp->attr.pbl_size) + return -ENOMEM; + + stag = mhp->attr.stag; + ret = write_tpt_entry(&rhp->rdev, 0, &stag, 1, mhp->attr.pdid, + FW_RI_STAG_NSMR, mhp->attr.perms, + mhp->attr.mw_bind_enable, mhp->attr.zbva, + mhp->attr.va_fbo, mhp->attr.len, shift - 12, + mhp->attr.pbl_size, mhp->attr.pbl_addr); + if (ret) + return ret; + + ret = finish_mem_reg(mhp, stag); + if (ret) + dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size, + mhp->attr.pbl_addr); + + return ret; +} + +static int alloc_pbl(struct c4iw_mr *mhp, int npages) +{ + mhp->attr.pbl_addr = c4iw_pblpool_alloc(&mhp->rhp->rdev, + npages << 3); + + if (!mhp->attr.pbl_addr) + return -ENOMEM; + + mhp->attr.pbl_size = npages; + + return 0; +} + +static int build_phys_page_list(struct ib_phys_buf *buffer_list, + int num_phys_buf, u64 *iova_start, + u64 *total_size, int *npages, + int *shift, __be64 **page_list) +{ + u64 mask; + int i, j, n; + + mask = 0; + *total_size = 0; + for (i = 0; i < num_phys_buf; ++i) { + if (i != 0 && buffer_list[i].addr & ~PAGE_MASK) + return -EINVAL; + if (i != 0 && i != num_phys_buf - 1 && + (buffer_list[i].size & ~PAGE_MASK)) + return -EINVAL; + *total_size += buffer_list[i].size; + if (i > 0) + mask |= buffer_list[i].addr; + else + mask |= buffer_list[i].addr & PAGE_MASK; + if (i != num_phys_buf - 1) + mask |= buffer_list[i].addr + buffer_list[i].size; + else + mask |= (buffer_list[i].addr + buffer_list[i].size + + PAGE_SIZE - 1) & PAGE_MASK; + } + + if (*total_size > 0xFFFFFFFFULL) + return -ENOMEM; + + /* Find largest page shift we can use to cover buffers */ + for (*shift = PAGE_SHIFT; *shift < 27; ++(*shift)) + if ((1ULL << *shift) & mask) + break; + + buffer_list[0].size += buffer_list[0].addr & ((1ULL << *shift) - 1); + buffer_list[0].addr &= ~0ull << *shift; + + *npages = 0; + for (i = 0; i < num_phys_buf; ++i) + *npages += (buffer_list[i].size + + (1ULL << *shift) - 1) >> *shift; + + if (!*npages) + return -EINVAL; + + *page_list = kmalloc(sizeof(u64) * *npages, GFP_KERNEL); + if (!*page_list) + return -ENOMEM; + + n = 0; + for (i = 0; i < num_phys_buf; ++i) + for (j = 0; + j < (buffer_list[i].size + (1ULL << *shift) - 1) >> *shift; + ++j) + (*page_list)[n++] = cpu_to_be64(buffer_list[i].addr + + ((u64) j << *shift)); + + PDBG("%s va 0x%llx mask 0x%llx shift %d len %lld pbl_size %d\n", + __func__, (unsigned long long)*iova_start, + (unsigned long long)mask, *shift, (unsigned long long)*total_size, + *npages); + + return 0; + +} + +int c4iw_reregister_phys_mem(struct ib_mr *mr, int mr_rereg_mask, + struct ib_pd *pd, struct ib_phys_buf *buffer_list, + int num_phys_buf, int acc, u64 *iova_start) +{ + + struct c4iw_mr mh, *mhp; + struct c4iw_pd *php; + struct c4iw_dev *rhp; + __be64 *page_list = NULL; + int shift = 0; + u64 total_size; + int npages; + int ret; + + PDBG("%s ib_mr %p ib_pd %p\n", __func__, mr, pd); + + /* There can be no memory windows */ + if (atomic_read(&mr->usecnt)) + return -EINVAL; + + mhp = to_c4iw_mr(mr); + rhp = mhp->rhp; + php = to_c4iw_pd(mr->pd); + + /* make sure we are on the same adapter */ + if (rhp != php->rhp) + return -EINVAL; + + memcpy(&mh, mhp, sizeof *mhp); + + if (mr_rereg_mask & IB_MR_REREG_PD) + php = to_c4iw_pd(pd); + if (mr_rereg_mask & IB_MR_REREG_ACCESS) { + mh.attr.perms = c4iw_ib_to_tpt_access(acc); + mh.attr.mw_bind_enable = (acc & IB_ACCESS_MW_BIND) == + IB_ACCESS_MW_BIND; + } + if (mr_rereg_mask & IB_MR_REREG_TRANS) { + ret = build_phys_page_list(buffer_list, num_phys_buf, + iova_start, + &total_size, &npages, + &shift, &page_list); + if (ret) + return ret; + } + + ret = reregister_mem(rhp, php, &mh, shift, npages); + kfree(page_list); + if (ret) + return ret; + if (mr_rereg_mask & IB_MR_REREG_PD) + mhp->attr.pdid = php->pdid; + if (mr_rereg_mask & IB_MR_REREG_ACCESS) + mhp->attr.perms = c4iw_ib_to_tpt_access(acc); + if (mr_rereg_mask & IB_MR_REREG_TRANS) { + mhp->attr.zbva = 0; + mhp->attr.va_fbo = *iova_start; + mhp->attr.page_size = shift - 12; + mhp->attr.len = (u32) total_size; + mhp->attr.pbl_size = npages; + } + + return 0; +} + +struct ib_mr *c4iw_register_phys_mem(struct ib_pd *pd, + struct ib_phys_buf *buffer_list, + int num_phys_buf, int acc, u64 *iova_start) +{ + __be64 *page_list; + int shift; + u64 total_size; + int npages; + struct c4iw_dev *rhp; + struct c4iw_pd *php; + struct c4iw_mr *mhp; + int ret; + + PDBG("%s ib_pd %p\n", __func__, pd); + php = to_c4iw_pd(pd); + rhp = php->rhp; + + mhp = kzalloc(sizeof(*mhp), GFP_KERNEL); + if (!mhp) + return ERR_PTR(-ENOMEM); + + mhp->rhp = rhp; + + /* First check that we have enough alignment */ + if ((*iova_start & ~PAGE_MASK) != (buffer_list[0].addr & ~PAGE_MASK)) { + ret = -EINVAL; + goto err; + } + + if (num_phys_buf > 1 && + ((buffer_list[0].addr + buffer_list[0].size) & ~PAGE_MASK)) { + ret = -EINVAL; + goto err; + } + + ret = build_phys_page_list(buffer_list, num_phys_buf, iova_start, + &total_size, &npages, &shift, + &page_list); + if (ret) + goto err; + + ret = alloc_pbl(mhp, npages); + if (ret) { + kfree(page_list); + goto err_pbl; + } + + ret = write_pbl(&mhp->rhp->rdev, page_list, mhp->attr.pbl_addr, + npages); + kfree(page_list); + if (ret) + goto err_pbl; + + mhp->attr.pdid = php->pdid; + mhp->attr.zbva = 0; + + mhp->attr.perms = c4iw_ib_to_tpt_access(acc); + mhp->attr.va_fbo = *iova_start; + mhp->attr.page_size = shift - 12; + + mhp->attr.len = (u32) total_size; + mhp->attr.pbl_size = npages; + ret = register_mem(rhp, php, mhp, shift); + if (ret) + goto err_pbl; + + return &mhp->ibmr; + +err_pbl: + c4iw_pblpool_free(&mhp->rhp->rdev, mhp->attr.pbl_addr, + mhp->attr.pbl_size << 3); + +err: + kfree(mhp); + return ERR_PTR(ret); + +} + +struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc) +{ + struct c4iw_dev *rhp; + struct c4iw_pd *php; + struct c4iw_mr *mhp; + int ret; + u32 stag = T4_STAG_UNSET; + + PDBG("%s ib_pd %p\n", __func__, pd); + php = to_c4iw_pd(pd); + rhp = php->rhp; + + mhp = kzalloc(sizeof(*mhp), GFP_KERNEL); + if (!mhp) + return ERR_PTR(-ENOMEM); + + mhp->rhp = rhp; + mhp->attr.pdid = php->pdid; + mhp->attr.perms = c4iw_ib_to_tpt_access(acc); + mhp->attr.mw_bind_enable = (acc&IB_ACCESS_MW_BIND) == IB_ACCESS_MW_BIND; + mhp->attr.zbva = 0; + mhp->attr.va_fbo = 0; + mhp->attr.page_size = 0; + mhp->attr.len = ~0UL; + mhp->attr.pbl_size = 0; + + ret = write_tpt_entry(&rhp->rdev, 0, &stag, 1, php->pdid, + FW_RI_STAG_NSMR, mhp->attr.perms, + mhp->attr.mw_bind_enable, 0, 0, ~0UL, 0, 0, 0); + if (ret) + goto err1; + + ret = finish_mem_reg(mhp, stag); + if (ret) + goto err2; + return &mhp->ibmr; +err2: + dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size, + mhp->attr.pbl_addr); +err1: + kfree(mhp); + return ERR_PTR(ret); +} + +struct ib_mr *c4iw_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt, int acc, struct ib_udata *udata) +{ + __be64 *pages; + int shift, n, len; + int i, j, k; + int err = 0; + struct ib_umem_chunk *chunk; + struct c4iw_dev *rhp; + struct c4iw_pd *php; + struct c4iw_mr *mhp; + + PDBG("%s ib_pd %p\n", __func__, pd); + + if (length == ~0ULL) + return ERR_PTR(-EINVAL); + + if ((length + start) < start) + return ERR_PTR(-EINVAL); + + php = to_c4iw_pd(pd); + rhp = php->rhp; + mhp = kzalloc(sizeof(*mhp), GFP_KERNEL); + if (!mhp) + return ERR_PTR(-ENOMEM); + + mhp->rhp = rhp; + + mhp->umem = ib_umem_get(pd->uobject->context, start, length, acc, 0); + if (IS_ERR(mhp->umem)) { + err = PTR_ERR(mhp->umem); + kfree(mhp); + return ERR_PTR(err); + } + + shift = ffs(mhp->umem->page_size) - 1; + + n = 0; + list_for_each_entry(chunk, &mhp->umem->chunk_list, list) + n += chunk->nents; + + err = alloc_pbl(mhp, n); + if (err) + goto err; + + pages = (__be64 *) __get_free_page(GFP_KERNEL); + if (!pages) { + err = -ENOMEM; + goto err_pbl; + } + + i = n = 0; + + list_for_each_entry(chunk, &mhp->umem->chunk_list, list) + for (j = 0; j < chunk->nmap; ++j) { + len = sg_dma_len(&chunk->page_list[j]) >> shift; + for (k = 0; k < len; ++k) { + pages[i++] = cpu_to_be64(sg_dma_address( + &chunk->page_list[j]) + + mhp->umem->page_size * k); + if (i == PAGE_SIZE / sizeof *pages) { + err = write_pbl(&mhp->rhp->rdev, + pages, + mhp->attr.pbl_addr + (n << 3), i); + if (err) + goto pbl_done; + n += i; + i = 0; + } + } + } + + if (i) + err = write_pbl(&mhp->rhp->rdev, pages, + mhp->attr.pbl_addr + (n << 3), i); + +pbl_done: + free_page((unsigned long) pages); + if (err) + goto err_pbl; + + mhp->attr.pdid = php->pdid; + mhp->attr.zbva = 0; + mhp->attr.perms = c4iw_ib_to_tpt_access(acc); + mhp->attr.va_fbo = virt; + mhp->attr.page_size = shift - 12; + mhp->attr.len = (u32) length; + + err = register_mem(rhp, php, mhp, shift); + if (err) + goto err_pbl; + + return &mhp->ibmr; + +err_pbl: + c4iw_pblpool_free(&mhp->rhp->rdev, mhp->attr.pbl_addr, + mhp->attr.pbl_size << 3); + +err: + ib_umem_release(mhp->umem); + kfree(mhp); + return ERR_PTR(err); +} + +struct ib_mw *c4iw_alloc_mw(struct ib_pd *pd) +{ + struct c4iw_dev *rhp; + struct c4iw_pd *php; + struct c4iw_mw *mhp; + u32 mmid; + u32 stag = 0; + int ret; + + php = to_c4iw_pd(pd); + rhp = php->rhp; + mhp = kzalloc(sizeof(*mhp), GFP_KERNEL); + if (!mhp) + return ERR_PTR(-ENOMEM); + ret = allocate_window(&rhp->rdev, &stag, php->pdid); + if (ret) { + kfree(mhp); + return ERR_PTR(ret); + } + mhp->rhp = rhp; + mhp->attr.pdid = php->pdid; + mhp->attr.type = FW_RI_STAG_MW; + mhp->attr.stag = stag; + mmid = (stag) >> 8; + mhp->ibmw.rkey = stag; + if (insert_handle(rhp, &rhp->mmidr, mhp, mmid)) { + deallocate_window(&rhp->rdev, mhp->attr.stag); + kfree(mhp); + return ERR_PTR(-ENOMEM); + } + PDBG("%s mmid 0x%x mhp %p stag 0x%x\n", __func__, mmid, mhp, stag); + return &(mhp->ibmw); +} + +int c4iw_dealloc_mw(struct ib_mw *mw) +{ + struct c4iw_dev *rhp; + struct c4iw_mw *mhp; + u32 mmid; + + mhp = to_c4iw_mw(mw); + rhp = mhp->rhp; + mmid = (mw->rkey) >> 8; + deallocate_window(&rhp->rdev, mhp->attr.stag); + remove_handle(rhp, &rhp->mmidr, mmid); + kfree(mhp); + PDBG("%s ib_mw %p mmid 0x%x ptr %p\n", __func__, mw, mmid, mhp); + return 0; +} + +struct ib_mr *c4iw_alloc_fast_reg_mr(struct ib_pd *pd, int pbl_depth) +{ + struct c4iw_dev *rhp; + struct c4iw_pd *php; + struct c4iw_mr *mhp; + u32 mmid; + u32 stag = 0; + int ret = 0; + + php = to_c4iw_pd(pd); + rhp = php->rhp; + mhp = kzalloc(sizeof(*mhp), GFP_KERNEL); + if (!mhp) + goto err; + + mhp->rhp = rhp; + ret = alloc_pbl(mhp, pbl_depth); + if (ret) + goto err1; + mhp->attr.pbl_size = pbl_depth; + ret = allocate_stag(&rhp->rdev, &stag, php->pdid, + mhp->attr.pbl_size, mhp->attr.pbl_addr); + if (ret) + goto err2; + mhp->attr.pdid = php->pdid; + mhp->attr.type = FW_RI_STAG_NSMR; + mhp->attr.stag = stag; + mhp->attr.state = 1; + mmid = (stag) >> 8; + mhp->ibmr.rkey = mhp->ibmr.lkey = stag; + if (insert_handle(rhp, &rhp->mmidr, mhp, mmid)) + goto err3; + + PDBG("%s mmid 0x%x mhp %p stag 0x%x\n", __func__, mmid, mhp, stag); + return &(mhp->ibmr); +err3: + dereg_mem(&rhp->rdev, stag, mhp->attr.pbl_size, + mhp->attr.pbl_addr); +err2: + c4iw_pblpool_free(&mhp->rhp->rdev, mhp->attr.pbl_addr, + mhp->attr.pbl_size << 3); +err1: + kfree(mhp); +err: + return ERR_PTR(ret); +} + +struct ib_fast_reg_page_list *c4iw_alloc_fastreg_pbl(struct ib_device *device, + int page_list_len) +{ + struct c4iw_fr_page_list *c4pl; + struct c4iw_dev *dev = to_c4iw_dev(device); + dma_addr_t dma_addr; + int size = sizeof *c4pl + page_list_len * sizeof(u64); + + if (page_list_len > T4_MAX_FR_DEPTH) + return ERR_PTR(-EINVAL); + + c4pl = dma_alloc_coherent(&dev->rdev.lldi.pdev->dev, size, + &dma_addr, GFP_KERNEL); + if (!c4pl) + return ERR_PTR(-ENOMEM); + + pci_unmap_addr_set(c4pl, mapping, dma_addr); + c4pl->dma_addr = dma_addr; + c4pl->dev = dev; + c4pl->size = size; + c4pl->ibpl.page_list = (u64 *)(c4pl + 1); + c4pl->ibpl.max_page_list_len = page_list_len; + + return &c4pl->ibpl; +} + +void c4iw_free_fastreg_pbl(struct ib_fast_reg_page_list *ibpl) +{ + struct c4iw_fr_page_list *c4pl = to_c4iw_fr_page_list(ibpl); + + dma_free_coherent(&c4pl->dev->rdev.lldi.pdev->dev, c4pl->size, + c4pl, pci_unmap_addr(c4pl, mapping)); +} + +int c4iw_dereg_mr(struct ib_mr *ib_mr) +{ + struct c4iw_dev *rhp; + struct c4iw_mr *mhp; + u32 mmid; + + PDBG("%s ib_mr %p\n", __func__, ib_mr); + /* There can be no memory windows */ + if (atomic_read(&ib_mr->usecnt)) + return -EINVAL; + + mhp = to_c4iw_mr(ib_mr); + rhp = mhp->rhp; + mmid = mhp->attr.stag >> 8; + dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size, + mhp->attr.pbl_addr); + if (mhp->attr.pbl_size) + c4iw_pblpool_free(&mhp->rhp->rdev, mhp->attr.pbl_addr, + mhp->attr.pbl_size << 3); + remove_handle(rhp, &rhp->mmidr, mmid); + if (mhp->kva) + kfree((void *) (unsigned long) mhp->kva); + if (mhp->umem) + ib_umem_release(mhp->umem); + PDBG("%s mmid 0x%x ptr %p\n", __func__, mmid, mhp); + kfree(mhp); + return 0; +} diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c new file mode 100644 index 000000000000..dfc49020bb9c --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/ethtool.h> +#include <linux/rtnetlink.h> +#include <linux/inetdevice.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <asm/byteorder.h> + +#include <rdma/iw_cm.h> +#include <rdma/ib_verbs.h> +#include <rdma/ib_smi.h> +#include <rdma/ib_umem.h> +#include <rdma/ib_user_verbs.h> + +#include "iw_cxgb4.h" + +static int fastreg_support; +module_param(fastreg_support, int, 0644); +MODULE_PARM_DESC(fastreg_support, "Advertise fastreg support (default=0)"); + +static int c4iw_modify_port(struct ib_device *ibdev, + u8 port, int port_modify_mask, + struct ib_port_modify *props) +{ + return -ENOSYS; +} + +static struct ib_ah *c4iw_ah_create(struct ib_pd *pd, + struct ib_ah_attr *ah_attr) +{ + return ERR_PTR(-ENOSYS); +} + +static int c4iw_ah_destroy(struct ib_ah *ah) +{ + return -ENOSYS; +} + +static int c4iw_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) +{ + return -ENOSYS; +} + +static int c4iw_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) +{ + return -ENOSYS; +} + +static int c4iw_process_mad(struct ib_device *ibdev, int mad_flags, + u8 port_num, struct ib_wc *in_wc, + struct ib_grh *in_grh, struct ib_mad *in_mad, + struct ib_mad *out_mad) +{ + return -ENOSYS; +} + +static int c4iw_dealloc_ucontext(struct ib_ucontext *context) +{ + struct c4iw_dev *rhp = to_c4iw_dev(context->device); + struct c4iw_ucontext *ucontext = to_c4iw_ucontext(context); + struct c4iw_mm_entry *mm, *tmp; + + PDBG("%s context %p\n", __func__, context); + list_for_each_entry_safe(mm, tmp, &ucontext->mmaps, entry) + kfree(mm); + c4iw_release_dev_ucontext(&rhp->rdev, &ucontext->uctx); + kfree(ucontext); + return 0; +} + +static struct ib_ucontext *c4iw_alloc_ucontext(struct ib_device *ibdev, + struct ib_udata *udata) +{ + struct c4iw_ucontext *context; + struct c4iw_dev *rhp = to_c4iw_dev(ibdev); + + PDBG("%s ibdev %p\n", __func__, ibdev); + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return ERR_PTR(-ENOMEM); + c4iw_init_dev_ucontext(&rhp->rdev, &context->uctx); + INIT_LIST_HEAD(&context->mmaps); + spin_lock_init(&context->mmap_lock); + return &context->ibucontext; +} + +static int c4iw_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) +{ + int len = vma->vm_end - vma->vm_start; + u32 key = vma->vm_pgoff << PAGE_SHIFT; + struct c4iw_rdev *rdev; + int ret = 0; + struct c4iw_mm_entry *mm; + struct c4iw_ucontext *ucontext; + u64 addr; + + PDBG("%s pgoff 0x%lx key 0x%x len %d\n", __func__, vma->vm_pgoff, + key, len); + + if (vma->vm_start & (PAGE_SIZE-1)) + return -EINVAL; + + rdev = &(to_c4iw_dev(context->device)->rdev); + ucontext = to_c4iw_ucontext(context); + + mm = remove_mmap(ucontext, key, len); + if (!mm) + return -EINVAL; + addr = mm->addr; + kfree(mm); + + if ((addr >= pci_resource_start(rdev->lldi.pdev, 2)) && + (addr < (pci_resource_start(rdev->lldi.pdev, 2) + + pci_resource_len(rdev->lldi.pdev, 2)))) { + + /* + * Map T4 DB register. + */ + if (vma->vm_flags & VM_READ) + return -EPERM; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND; + vma->vm_flags &= ~VM_MAYREAD; + ret = io_remap_pfn_range(vma, vma->vm_start, + addr >> PAGE_SHIFT, + len, vma->vm_page_prot); + } else { + + /* + * Map WQ or CQ contig dma memory... + */ + ret = remap_pfn_range(vma, vma->vm_start, + addr >> PAGE_SHIFT, + len, vma->vm_page_prot); + } + + return ret; +} + +static int c4iw_deallocate_pd(struct ib_pd *pd) +{ + struct c4iw_dev *rhp; + struct c4iw_pd *php; + + php = to_c4iw_pd(pd); + rhp = php->rhp; + PDBG("%s ibpd %p pdid 0x%x\n", __func__, pd, php->pdid); + c4iw_put_resource(&rhp->rdev.resource.pdid_fifo, php->pdid, + &rhp->rdev.resource.pdid_fifo_lock); + kfree(php); + return 0; +} + +static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev, + struct ib_ucontext *context, + struct ib_udata *udata) +{ + struct c4iw_pd *php; + u32 pdid; + struct c4iw_dev *rhp; + + PDBG("%s ibdev %p\n", __func__, ibdev); + rhp = (struct c4iw_dev *) ibdev; + pdid = c4iw_get_resource(&rhp->rdev.resource.pdid_fifo, + &rhp->rdev.resource.pdid_fifo_lock); + if (!pdid) + return ERR_PTR(-EINVAL); + php = kzalloc(sizeof(*php), GFP_KERNEL); + if (!php) { + c4iw_put_resource(&rhp->rdev.resource.pdid_fifo, pdid, + &rhp->rdev.resource.pdid_fifo_lock); + return ERR_PTR(-ENOMEM); + } + php->pdid = pdid; + php->rhp = rhp; + if (context) { + if (ib_copy_to_udata(udata, &php->pdid, sizeof(u32))) { + c4iw_deallocate_pd(&php->ibpd); + return ERR_PTR(-EFAULT); + } + } + PDBG("%s pdid 0x%0x ptr 0x%p\n", __func__, pdid, php); + return &php->ibpd; +} + +static int c4iw_query_pkey(struct ib_device *ibdev, u8 port, u16 index, + u16 *pkey) +{ + PDBG("%s ibdev %p\n", __func__, ibdev); + *pkey = 0; + return 0; +} + +static int c4iw_query_gid(struct ib_device *ibdev, u8 port, int index, + union ib_gid *gid) +{ + struct c4iw_dev *dev; + + PDBG("%s ibdev %p, port %d, index %d, gid %p\n", + __func__, ibdev, port, index, gid); + dev = to_c4iw_dev(ibdev); + BUG_ON(port == 0); + memset(&(gid->raw[0]), 0, sizeof(gid->raw)); + memcpy(&(gid->raw[0]), dev->rdev.lldi.ports[port-1]->dev_addr, 6); + return 0; +} + +static int c4iw_query_device(struct ib_device *ibdev, + struct ib_device_attr *props) +{ + + struct c4iw_dev *dev; + PDBG("%s ibdev %p\n", __func__, ibdev); + + dev = to_c4iw_dev(ibdev); + memset(props, 0, sizeof *props); + memcpy(&props->sys_image_guid, dev->rdev.lldi.ports[0]->dev_addr, 6); + props->hw_ver = dev->rdev.lldi.adapter_type; + props->fw_ver = dev->rdev.lldi.fw_vers; + props->device_cap_flags = dev->device_cap_flags; + props->page_size_cap = T4_PAGESIZE_MASK; + props->vendor_id = (u32)dev->rdev.lldi.pdev->vendor; + props->vendor_part_id = (u32)dev->rdev.lldi.pdev->device; + props->max_mr_size = T4_MAX_MR_SIZE; + props->max_qp = T4_MAX_NUM_QP; + props->max_qp_wr = T4_MAX_QP_DEPTH; + props->max_sge = T4_MAX_RECV_SGE; + props->max_sge_rd = 1; + props->max_qp_rd_atom = c4iw_max_read_depth; + props->max_qp_init_rd_atom = c4iw_max_read_depth; + props->max_cq = T4_MAX_NUM_CQ; + props->max_cqe = T4_MAX_CQ_DEPTH; + props->max_mr = c4iw_num_stags(&dev->rdev); + props->max_pd = T4_MAX_NUM_PD; + props->local_ca_ack_delay = 0; + props->max_fast_reg_page_list_len = T4_MAX_FR_DEPTH; + + return 0; +} + +static int c4iw_query_port(struct ib_device *ibdev, u8 port, + struct ib_port_attr *props) +{ + struct c4iw_dev *dev; + struct net_device *netdev; + struct in_device *inetdev; + + PDBG("%s ibdev %p\n", __func__, ibdev); + + dev = to_c4iw_dev(ibdev); + netdev = dev->rdev.lldi.ports[port-1]; + + memset(props, 0, sizeof(struct ib_port_attr)); + props->max_mtu = IB_MTU_4096; + if (netdev->mtu >= 4096) + props->active_mtu = IB_MTU_4096; + else if (netdev->mtu >= 2048) + props->active_mtu = IB_MTU_2048; + else if (netdev->mtu >= 1024) + props->active_mtu = IB_MTU_1024; + else if (netdev->mtu >= 512) + props->active_mtu = IB_MTU_512; + else + props->active_mtu = IB_MTU_256; + + if (!netif_carrier_ok(netdev)) + props->state = IB_PORT_DOWN; + else { + inetdev = in_dev_get(netdev); + if (inetdev) { + if (inetdev->ifa_list) + props->state = IB_PORT_ACTIVE; + else + props->state = IB_PORT_INIT; + in_dev_put(inetdev); + } else + props->state = IB_PORT_INIT; + } + + props->port_cap_flags = + IB_PORT_CM_SUP | + IB_PORT_SNMP_TUNNEL_SUP | + IB_PORT_REINIT_SUP | + IB_PORT_DEVICE_MGMT_SUP | + IB_PORT_VENDOR_CLASS_SUP | IB_PORT_BOOT_MGMT_SUP; + props->gid_tbl_len = 1; + props->pkey_tbl_len = 1; + props->active_width = 2; + props->active_speed = 2; + props->max_msg_sz = -1; + + return 0; +} + +static ssize_t show_rev(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev, + ibdev.dev); + PDBG("%s dev 0x%p\n", __func__, dev); + return sprintf(buf, "%d\n", c4iw_dev->rdev.lldi.adapter_type); +} + +static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev, + ibdev.dev); + PDBG("%s dev 0x%p\n", __func__, dev); + + return sprintf(buf, "%u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_GET(c4iw_dev->rdev.lldi.fw_vers), + FW_HDR_FW_VER_MINOR_GET(c4iw_dev->rdev.lldi.fw_vers), + FW_HDR_FW_VER_MICRO_GET(c4iw_dev->rdev.lldi.fw_vers), + FW_HDR_FW_VER_BUILD_GET(c4iw_dev->rdev.lldi.fw_vers)); +} + +static ssize_t show_hca(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev, + ibdev.dev); + struct ethtool_drvinfo info; + struct net_device *lldev = c4iw_dev->rdev.lldi.ports[0]; + + PDBG("%s dev 0x%p\n", __func__, dev); + lldev->ethtool_ops->get_drvinfo(lldev, &info); + return sprintf(buf, "%s\n", info.driver); +} + +static ssize_t show_board(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev, + ibdev.dev); + PDBG("%s dev 0x%p\n", __func__, dev); + return sprintf(buf, "%x.%x\n", c4iw_dev->rdev.lldi.pdev->vendor, + c4iw_dev->rdev.lldi.pdev->device); +} + +static int c4iw_get_mib(struct ib_device *ibdev, + union rdma_protocol_stats *stats) +{ + return -ENOSYS; +} + +static DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL); +static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL); +static DEVICE_ATTR(hca_type, S_IRUGO, show_hca, NULL); +static DEVICE_ATTR(board_id, S_IRUGO, show_board, NULL); + +static struct device_attribute *c4iw_class_attributes[] = { + &dev_attr_hw_rev, + &dev_attr_fw_ver, + &dev_attr_hca_type, + &dev_attr_board_id, +}; + +int c4iw_register_device(struct c4iw_dev *dev) +{ + int ret; + int i; + + PDBG("%s c4iw_dev %p\n", __func__, dev); + BUG_ON(!dev->rdev.lldi.ports[0]); + strlcpy(dev->ibdev.name, "cxgb4_%d", IB_DEVICE_NAME_MAX); + memset(&dev->ibdev.node_guid, 0, sizeof(dev->ibdev.node_guid)); + memcpy(&dev->ibdev.node_guid, dev->rdev.lldi.ports[0]->dev_addr, 6); + dev->ibdev.owner = THIS_MODULE; + dev->device_cap_flags = IB_DEVICE_LOCAL_DMA_LKEY | IB_DEVICE_MEM_WINDOW; + if (fastreg_support) + dev->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS; + dev->ibdev.local_dma_lkey = 0; + dev->ibdev.uverbs_cmd_mask = + (1ull << IB_USER_VERBS_CMD_GET_CONTEXT) | + (1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) | + (1ull << IB_USER_VERBS_CMD_QUERY_PORT) | + (1ull << IB_USER_VERBS_CMD_ALLOC_PD) | + (1ull << IB_USER_VERBS_CMD_DEALLOC_PD) | + (1ull << IB_USER_VERBS_CMD_REG_MR) | + (1ull << IB_USER_VERBS_CMD_DEREG_MR) | + (1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) | + (1ull << IB_USER_VERBS_CMD_CREATE_CQ) | + (1ull << IB_USER_VERBS_CMD_DESTROY_CQ) | + (1ull << IB_USER_VERBS_CMD_REQ_NOTIFY_CQ) | + (1ull << IB_USER_VERBS_CMD_CREATE_QP) | + (1ull << IB_USER_VERBS_CMD_MODIFY_QP) | + (1ull << IB_USER_VERBS_CMD_POLL_CQ) | + (1ull << IB_USER_VERBS_CMD_DESTROY_QP) | + (1ull << IB_USER_VERBS_CMD_POST_SEND) | + (1ull << IB_USER_VERBS_CMD_POST_RECV); + dev->ibdev.node_type = RDMA_NODE_RNIC; + memcpy(dev->ibdev.node_desc, C4IW_NODE_DESC, sizeof(C4IW_NODE_DESC)); + dev->ibdev.phys_port_cnt = dev->rdev.lldi.nports; + dev->ibdev.num_comp_vectors = 1; + dev->ibdev.dma_device = &(dev->rdev.lldi.pdev->dev); + dev->ibdev.query_device = c4iw_query_device; + dev->ibdev.query_port = c4iw_query_port; + dev->ibdev.modify_port = c4iw_modify_port; + dev->ibdev.query_pkey = c4iw_query_pkey; + dev->ibdev.query_gid = c4iw_query_gid; + dev->ibdev.alloc_ucontext = c4iw_alloc_ucontext; + dev->ibdev.dealloc_ucontext = c4iw_dealloc_ucontext; + dev->ibdev.mmap = c4iw_mmap; + dev->ibdev.alloc_pd = c4iw_allocate_pd; + dev->ibdev.dealloc_pd = c4iw_deallocate_pd; + dev->ibdev.create_ah = c4iw_ah_create; + dev->ibdev.destroy_ah = c4iw_ah_destroy; + dev->ibdev.create_qp = c4iw_create_qp; + dev->ibdev.modify_qp = c4iw_ib_modify_qp; + dev->ibdev.destroy_qp = c4iw_destroy_qp; + dev->ibdev.create_cq = c4iw_create_cq; + dev->ibdev.destroy_cq = c4iw_destroy_cq; + dev->ibdev.resize_cq = c4iw_resize_cq; + dev->ibdev.poll_cq = c4iw_poll_cq; + dev->ibdev.get_dma_mr = c4iw_get_dma_mr; + dev->ibdev.reg_phys_mr = c4iw_register_phys_mem; + dev->ibdev.rereg_phys_mr = c4iw_reregister_phys_mem; + dev->ibdev.reg_user_mr = c4iw_reg_user_mr; + dev->ibdev.dereg_mr = c4iw_dereg_mr; + dev->ibdev.alloc_mw = c4iw_alloc_mw; + dev->ibdev.bind_mw = c4iw_bind_mw; + dev->ibdev.dealloc_mw = c4iw_dealloc_mw; + dev->ibdev.alloc_fast_reg_mr = c4iw_alloc_fast_reg_mr; + dev->ibdev.alloc_fast_reg_page_list = c4iw_alloc_fastreg_pbl; + dev->ibdev.free_fast_reg_page_list = c4iw_free_fastreg_pbl; + dev->ibdev.attach_mcast = c4iw_multicast_attach; + dev->ibdev.detach_mcast = c4iw_multicast_detach; + dev->ibdev.process_mad = c4iw_process_mad; + dev->ibdev.req_notify_cq = c4iw_arm_cq; + dev->ibdev.post_send = c4iw_post_send; + dev->ibdev.post_recv = c4iw_post_receive; + dev->ibdev.get_protocol_stats = c4iw_get_mib; + + dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL); + if (!dev->ibdev.iwcm) + return -ENOMEM; + + dev->ibdev.iwcm->connect = c4iw_connect; + dev->ibdev.iwcm->accept = c4iw_accept_cr; + dev->ibdev.iwcm->reject = c4iw_reject_cr; + dev->ibdev.iwcm->create_listen = c4iw_create_listen; + dev->ibdev.iwcm->destroy_listen = c4iw_destroy_listen; + dev->ibdev.iwcm->add_ref = c4iw_qp_add_ref; + dev->ibdev.iwcm->rem_ref = c4iw_qp_rem_ref; + dev->ibdev.iwcm->get_qp = c4iw_get_qp; + + ret = ib_register_device(&dev->ibdev); + if (ret) + goto bail1; + + for (i = 0; i < ARRAY_SIZE(c4iw_class_attributes); ++i) { + ret = device_create_file(&dev->ibdev.dev, + c4iw_class_attributes[i]); + if (ret) + goto bail2; + } + return 0; +bail2: + ib_unregister_device(&dev->ibdev); +bail1: + kfree(dev->ibdev.iwcm); + return ret; +} + +void c4iw_unregister_device(struct c4iw_dev *dev) +{ + int i; + + PDBG("%s c4iw_dev %p\n", __func__, dev); + for (i = 0; i < ARRAY_SIZE(c4iw_class_attributes); ++i) + device_remove_file(&dev->ibdev.dev, + c4iw_class_attributes[i]); + ib_unregister_device(&dev->ibdev); + kfree(dev->ibdev.iwcm); + return; +} diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c new file mode 100644 index 000000000000..83a01dc0c4c1 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -0,0 +1,1577 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + */ +#include "iw_cxgb4.h" + +static int destroy_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, + struct c4iw_dev_ucontext *uctx) +{ + /* + * uP clears EQ contexts when the connection exits rdma mode, + * so no need to post a RESET WR for these EQs. + */ + dma_free_coherent(&(rdev->lldi.pdev->dev), + wq->rq.memsize, wq->rq.queue, + pci_unmap_addr(&wq->rq, mapping)); + dma_free_coherent(&(rdev->lldi.pdev->dev), + wq->sq.memsize, wq->sq.queue, + pci_unmap_addr(&wq->sq, mapping)); + c4iw_rqtpool_free(rdev, wq->rq.rqt_hwaddr, wq->rq.rqt_size); + kfree(wq->rq.sw_rq); + kfree(wq->sq.sw_sq); + c4iw_put_qpid(rdev, wq->rq.qid, uctx); + c4iw_put_qpid(rdev, wq->sq.qid, uctx); + return 0; +} + +static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, + struct t4_cq *rcq, struct t4_cq *scq, + struct c4iw_dev_ucontext *uctx) +{ + int user = (uctx != &rdev->uctx); + struct fw_ri_res_wr *res_wr; + struct fw_ri_res *res; + int wr_len; + struct c4iw_wr_wait wr_wait; + struct sk_buff *skb; + int ret; + int eqsize; + + wq->sq.qid = c4iw_get_qpid(rdev, uctx); + if (!wq->sq.qid) + return -ENOMEM; + + wq->rq.qid = c4iw_get_qpid(rdev, uctx); + if (!wq->rq.qid) + goto err1; + + if (!user) { + wq->sq.sw_sq = kzalloc(wq->sq.size * sizeof *wq->sq.sw_sq, + GFP_KERNEL); + if (!wq->sq.sw_sq) + goto err2; + + wq->rq.sw_rq = kzalloc(wq->rq.size * sizeof *wq->rq.sw_rq, + GFP_KERNEL); + if (!wq->rq.sw_rq) + goto err3; + } + + /* + * RQT must be a power of 2. + */ + wq->rq.rqt_size = roundup_pow_of_two(wq->rq.size); + wq->rq.rqt_hwaddr = c4iw_rqtpool_alloc(rdev, wq->rq.rqt_size); + if (!wq->rq.rqt_hwaddr) + goto err4; + + wq->sq.queue = dma_alloc_coherent(&(rdev->lldi.pdev->dev), + wq->sq.memsize, &(wq->sq.dma_addr), + GFP_KERNEL); + if (!wq->sq.queue) + goto err5; + memset(wq->sq.queue, 0, wq->sq.memsize); + pci_unmap_addr_set(&wq->sq, mapping, wq->sq.dma_addr); + + wq->rq.queue = dma_alloc_coherent(&(rdev->lldi.pdev->dev), + wq->rq.memsize, &(wq->rq.dma_addr), + GFP_KERNEL); + if (!wq->rq.queue) + goto err6; + PDBG("%s sq base va 0x%p pa 0x%llx rq base va 0x%p pa 0x%llx\n", + __func__, wq->sq.queue, + (unsigned long long)virt_to_phys(wq->sq.queue), + wq->rq.queue, + (unsigned long long)virt_to_phys(wq->rq.queue)); + memset(wq->rq.queue, 0, wq->rq.memsize); + pci_unmap_addr_set(&wq->rq, mapping, wq->rq.dma_addr); + + wq->db = rdev->lldi.db_reg; + wq->gts = rdev->lldi.gts_reg; + if (user) { + wq->sq.udb = (u64)pci_resource_start(rdev->lldi.pdev, 2) + + (wq->sq.qid << rdev->qpshift); + wq->sq.udb &= PAGE_MASK; + wq->rq.udb = (u64)pci_resource_start(rdev->lldi.pdev, 2) + + (wq->rq.qid << rdev->qpshift); + wq->rq.udb &= PAGE_MASK; + } + wq->rdev = rdev; + wq->rq.msn = 1; + + /* build fw_ri_res_wr */ + wr_len = sizeof *res_wr + 2 * sizeof *res; + + skb = alloc_skb(wr_len, GFP_KERNEL | __GFP_NOFAIL); + if (!skb) { + ret = -ENOMEM; + goto err7; + } + set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); + + res_wr = (struct fw_ri_res_wr *)__skb_put(skb, wr_len); + memset(res_wr, 0, wr_len); + res_wr->op_nres = cpu_to_be32( + FW_WR_OP(FW_RI_RES_WR) | + V_FW_RI_RES_WR_NRES(2) | + FW_WR_COMPL(1)); + res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); + res_wr->cookie = (u64)&wr_wait; + res = res_wr->res; + res->u.sqrq.restype = FW_RI_RES_TYPE_SQ; + res->u.sqrq.op = FW_RI_RES_OP_WRITE; + + /* + * eqsize is the number of 64B entries plus the status page size. + */ + eqsize = wq->sq.size * T4_SQ_NUM_SLOTS + T4_EQ_STATUS_ENTRIES; + + res->u.sqrq.fetchszm_to_iqid = cpu_to_be32( + V_FW_RI_RES_WR_HOSTFCMODE(0) | /* no host cidx updates */ + V_FW_RI_RES_WR_CPRIO(0) | /* don't keep in chip cache */ + V_FW_RI_RES_WR_PCIECHN(0) | /* set by uP at ri_init time */ + V_FW_RI_RES_WR_IQID(scq->cqid)); + res->u.sqrq.dcaen_to_eqsize = cpu_to_be32( + V_FW_RI_RES_WR_DCAEN(0) | + V_FW_RI_RES_WR_DCACPU(0) | + V_FW_RI_RES_WR_FBMIN(3) | + V_FW_RI_RES_WR_FBMAX(3) | + V_FW_RI_RES_WR_CIDXFTHRESHO(0) | + V_FW_RI_RES_WR_CIDXFTHRESH(0) | + V_FW_RI_RES_WR_EQSIZE(eqsize)); + res->u.sqrq.eqid = cpu_to_be32(wq->sq.qid); + res->u.sqrq.eqaddr = cpu_to_be64(wq->sq.dma_addr); + res++; + res->u.sqrq.restype = FW_RI_RES_TYPE_RQ; + res->u.sqrq.op = FW_RI_RES_OP_WRITE; + + /* + * eqsize is the number of 64B entries plus the status page size. + */ + eqsize = wq->rq.size * T4_RQ_NUM_SLOTS + T4_EQ_STATUS_ENTRIES; + res->u.sqrq.fetchszm_to_iqid = cpu_to_be32( + V_FW_RI_RES_WR_HOSTFCMODE(0) | /* no host cidx updates */ + V_FW_RI_RES_WR_CPRIO(0) | /* don't keep in chip cache */ + V_FW_RI_RES_WR_PCIECHN(0) | /* set by uP at ri_init time */ + V_FW_RI_RES_WR_IQID(rcq->cqid)); + res->u.sqrq.dcaen_to_eqsize = cpu_to_be32( + V_FW_RI_RES_WR_DCAEN(0) | + V_FW_RI_RES_WR_DCACPU(0) | + V_FW_RI_RES_WR_FBMIN(3) | + V_FW_RI_RES_WR_FBMAX(3) | + V_FW_RI_RES_WR_CIDXFTHRESHO(0) | + V_FW_RI_RES_WR_CIDXFTHRESH(0) | + V_FW_RI_RES_WR_EQSIZE(eqsize)); + res->u.sqrq.eqid = cpu_to_be32(wq->rq.qid); + res->u.sqrq.eqaddr = cpu_to_be64(wq->rq.dma_addr); + + c4iw_init_wr_wait(&wr_wait); + + ret = c4iw_ofld_send(rdev, skb); + if (ret) + goto err7; + wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); + if (!wr_wait.done) { + printk(KERN_ERR MOD "Device %s not responding!\n", + pci_name(rdev->lldi.pdev)); + rdev->flags = T4_FATAL_ERROR; + ret = -EIO; + } else + ret = wr_wait.ret; + if (ret) + goto err7; + + PDBG("%s sqid 0x%x rqid 0x%x kdb 0x%p squdb 0x%llx rqudb 0x%llx\n", + __func__, wq->sq.qid, wq->rq.qid, wq->db, + (unsigned long long)wq->sq.udb, (unsigned long long)wq->rq.udb); + + return 0; +err7: + dma_free_coherent(&(rdev->lldi.pdev->dev), + wq->rq.memsize, wq->rq.queue, + pci_unmap_addr(&wq->rq, mapping)); +err6: + dma_free_coherent(&(rdev->lldi.pdev->dev), + wq->sq.memsize, wq->sq.queue, + pci_unmap_addr(&wq->sq, mapping)); +err5: + c4iw_rqtpool_free(rdev, wq->rq.rqt_hwaddr, wq->rq.rqt_size); +err4: + kfree(wq->rq.sw_rq); +err3: + kfree(wq->sq.sw_sq); +err2: + c4iw_put_qpid(rdev, wq->rq.qid, uctx); +err1: + c4iw_put_qpid(rdev, wq->sq.qid, uctx); + return -ENOMEM; +} + +static int build_rdma_send(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16) +{ + int i; + u32 plen; + int size; + u8 *datap; + + if (wr->num_sge > T4_MAX_SEND_SGE) + return -EINVAL; + switch (wr->opcode) { + case IB_WR_SEND: + if (wr->send_flags & IB_SEND_SOLICITED) + wqe->send.sendop_pkd = cpu_to_be32( + V_FW_RI_SEND_WR_SENDOP(FW_RI_SEND_WITH_SE)); + else + wqe->send.sendop_pkd = cpu_to_be32( + V_FW_RI_SEND_WR_SENDOP(FW_RI_SEND)); + wqe->send.stag_inv = 0; + break; + case IB_WR_SEND_WITH_INV: + if (wr->send_flags & IB_SEND_SOLICITED) + wqe->send.sendop_pkd = cpu_to_be32( + V_FW_RI_SEND_WR_SENDOP(FW_RI_SEND_WITH_SE_INV)); + else + wqe->send.sendop_pkd = cpu_to_be32( + V_FW_RI_SEND_WR_SENDOP(FW_RI_SEND_WITH_INV)); + wqe->send.stag_inv = cpu_to_be32(wr->ex.invalidate_rkey); + break; + + default: + return -EINVAL; + } + plen = 0; + if (wr->num_sge) { + if (wr->send_flags & IB_SEND_INLINE) { + datap = (u8 *)wqe->send.u.immd_src[0].data; + for (i = 0; i < wr->num_sge; i++) { + if ((plen + wr->sg_list[i].length) > + T4_MAX_SEND_INLINE) { + return -EMSGSIZE; + } + plen += wr->sg_list[i].length; + memcpy(datap, + (void *)(unsigned long)wr->sg_list[i].addr, + wr->sg_list[i].length); + datap += wr->sg_list[i].length; + } + wqe->send.u.immd_src[0].op = FW_RI_DATA_IMMD; + wqe->send.u.immd_src[0].r1 = 0; + wqe->send.u.immd_src[0].r2 = 0; + wqe->send.u.immd_src[0].immdlen = cpu_to_be32(plen); + size = sizeof wqe->send + sizeof(struct fw_ri_immd) + + plen; + } else { + for (i = 0; i < wr->num_sge; i++) { + if ((plen + wr->sg_list[i].length) < plen) + return -EMSGSIZE; + plen += wr->sg_list[i].length; + wqe->send.u.isgl_src[0].sge[i].stag = + cpu_to_be32(wr->sg_list[i].lkey); + wqe->send.u.isgl_src[0].sge[i].len = + cpu_to_be32(wr->sg_list[i].length); + wqe->send.u.isgl_src[0].sge[i].to = + cpu_to_be64(wr->sg_list[i].addr); + } + wqe->send.u.isgl_src[0].op = FW_RI_DATA_ISGL; + wqe->send.u.isgl_src[0].r1 = 0; + wqe->send.u.isgl_src[0].nsge = cpu_to_be16(wr->num_sge); + wqe->send.u.isgl_src[0].r2 = 0; + size = sizeof wqe->send + sizeof(struct fw_ri_isgl) + + wr->num_sge * sizeof(struct fw_ri_sge); + } + } else { + wqe->send.u.immd_src[0].op = FW_RI_DATA_IMMD; + wqe->send.u.immd_src[0].r1 = 0; + wqe->send.u.immd_src[0].r2 = 0; + wqe->send.u.immd_src[0].immdlen = 0; + size = sizeof wqe->send + sizeof(struct fw_ri_immd); + } + *len16 = DIV_ROUND_UP(size, 16); + wqe->send.plen = cpu_to_be32(plen); + return 0; +} + +static int build_rdma_write(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16) +{ + int i; + u32 plen; + int size; + u8 *datap; + + if (wr->num_sge > T4_MAX_WRITE_SGE) + return -EINVAL; + wqe->write.r2 = 0; + wqe->write.stag_sink = cpu_to_be32(wr->wr.rdma.rkey); + wqe->write.to_sink = cpu_to_be64(wr->wr.rdma.remote_addr); + plen = 0; + if (wr->num_sge) { + if (wr->send_flags & IB_SEND_INLINE) { + datap = (u8 *)wqe->write.u.immd_src[0].data; + for (i = 0; i < wr->num_sge; i++) { + if ((plen + wr->sg_list[i].length) > + T4_MAX_WRITE_INLINE) { + return -EMSGSIZE; + } + plen += wr->sg_list[i].length; + memcpy(datap, + (void *)(unsigned long)wr->sg_list[i].addr, + wr->sg_list[i].length); + datap += wr->sg_list[i].length; + } + wqe->write.u.immd_src[0].op = FW_RI_DATA_IMMD; + wqe->write.u.immd_src[0].r1 = 0; + wqe->write.u.immd_src[0].r2 = 0; + wqe->write.u.immd_src[0].immdlen = cpu_to_be32(plen); + size = sizeof wqe->write + sizeof(struct fw_ri_immd) + + plen; + } else { + for (i = 0; i < wr->num_sge; i++) { + if ((plen + wr->sg_list[i].length) < plen) + return -EMSGSIZE; + plen += wr->sg_list[i].length; + wqe->write.u.isgl_src[0].sge[i].stag = + cpu_to_be32(wr->sg_list[i].lkey); + wqe->write.u.isgl_src[0].sge[i].len = + cpu_to_be32(wr->sg_list[i].length); + wqe->write.u.isgl_src[0].sge[i].to = + cpu_to_be64(wr->sg_list[i].addr); + } + wqe->write.u.isgl_src[0].op = FW_RI_DATA_ISGL; + wqe->write.u.isgl_src[0].r1 = 0; + wqe->write.u.isgl_src[0].nsge = + cpu_to_be16(wr->num_sge); + wqe->write.u.isgl_src[0].r2 = 0; + size = sizeof wqe->write + sizeof(struct fw_ri_isgl) + + wr->num_sge * sizeof(struct fw_ri_sge); + } + } else { + wqe->write.u.immd_src[0].op = FW_RI_DATA_IMMD; + wqe->write.u.immd_src[0].r1 = 0; + wqe->write.u.immd_src[0].r2 = 0; + wqe->write.u.immd_src[0].immdlen = 0; + size = sizeof wqe->write + sizeof(struct fw_ri_immd); + } + *len16 = DIV_ROUND_UP(size, 16); + wqe->write.plen = cpu_to_be32(plen); + return 0; +} + +static int build_rdma_read(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16) +{ + if (wr->num_sge > 1) + return -EINVAL; + if (wr->num_sge) { + wqe->read.stag_src = cpu_to_be32(wr->wr.rdma.rkey); + wqe->read.to_src_hi = cpu_to_be32((u32)(wr->wr.rdma.remote_addr + >> 32)); + wqe->read.to_src_lo = cpu_to_be32((u32)wr->wr.rdma.remote_addr); + wqe->read.stag_sink = cpu_to_be32(wr->sg_list[0].lkey); + wqe->read.plen = cpu_to_be32(wr->sg_list[0].length); + wqe->read.to_sink_hi = cpu_to_be32((u32)(wr->sg_list[0].addr + >> 32)); + wqe->read.to_sink_lo = cpu_to_be32((u32)(wr->sg_list[0].addr)); + } else { + wqe->read.stag_src = cpu_to_be32(2); + wqe->read.to_src_hi = 0; + wqe->read.to_src_lo = 0; + wqe->read.stag_sink = cpu_to_be32(2); + wqe->read.plen = 0; + wqe->read.to_sink_hi = 0; + wqe->read.to_sink_lo = 0; + } + wqe->read.r2 = 0; + wqe->read.r5 = 0; + *len16 = DIV_ROUND_UP(sizeof wqe->read, 16); + return 0; +} + +static int build_rdma_recv(struct c4iw_qp *qhp, union t4_recv_wr *wqe, + struct ib_recv_wr *wr, u8 *len16) +{ + int i; + int plen = 0; + + for (i = 0; i < wr->num_sge; i++) { + if ((plen + wr->sg_list[i].length) < plen) + return -EMSGSIZE; + plen += wr->sg_list[i].length; + wqe->recv.isgl.sge[i].stag = + cpu_to_be32(wr->sg_list[i].lkey); + wqe->recv.isgl.sge[i].len = + cpu_to_be32(wr->sg_list[i].length); + wqe->recv.isgl.sge[i].to = + cpu_to_be64(wr->sg_list[i].addr); + } + for (; i < T4_MAX_RECV_SGE; i++) { + wqe->recv.isgl.sge[i].stag = 0; + wqe->recv.isgl.sge[i].len = 0; + wqe->recv.isgl.sge[i].to = 0; + } + wqe->recv.isgl.op = FW_RI_DATA_ISGL; + wqe->recv.isgl.r1 = 0; + wqe->recv.isgl.nsge = cpu_to_be16(wr->num_sge); + wqe->recv.isgl.r2 = 0; + *len16 = DIV_ROUND_UP(sizeof wqe->recv + + wr->num_sge * sizeof(struct fw_ri_sge), 16); + return 0; +} + +static int build_fastreg(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16) +{ + + struct fw_ri_immd *imdp; + __be64 *p; + int i; + int pbllen = roundup(wr->wr.fast_reg.page_list_len * sizeof(u64), 32); + + if (wr->wr.fast_reg.page_list_len > T4_MAX_FR_DEPTH) + return -EINVAL; + + wqe->fr.qpbinde_to_dcacpu = 0; + wqe->fr.pgsz_shift = wr->wr.fast_reg.page_shift - 12; + wqe->fr.addr_type = FW_RI_VA_BASED_TO; + wqe->fr.mem_perms = c4iw_ib_to_tpt_access(wr->wr.fast_reg.access_flags); + wqe->fr.len_hi = 0; + wqe->fr.len_lo = cpu_to_be32(wr->wr.fast_reg.length); + wqe->fr.stag = cpu_to_be32(wr->wr.fast_reg.rkey); + wqe->fr.va_hi = cpu_to_be32(wr->wr.fast_reg.iova_start >> 32); + wqe->fr.va_lo_fbo = cpu_to_be32(wr->wr.fast_reg.iova_start & + 0xffffffff); + if (pbllen > T4_MAX_FR_IMMD) { + struct c4iw_fr_page_list *c4pl = + to_c4iw_fr_page_list(wr->wr.fast_reg.page_list); + struct fw_ri_dsgl *sglp; + + sglp = (struct fw_ri_dsgl *)(&wqe->fr + 1); + sglp->op = FW_RI_DATA_DSGL; + sglp->r1 = 0; + sglp->nsge = cpu_to_be16(1); + sglp->addr0 = cpu_to_be64(c4pl->dma_addr); + sglp->len0 = cpu_to_be32(pbllen); + + *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *sglp, 16); + } else { + imdp = (struct fw_ri_immd *)(&wqe->fr + 1); + imdp->op = FW_RI_DATA_IMMD; + imdp->r1 = 0; + imdp->r2 = 0; + imdp->immdlen = cpu_to_be32(pbllen); + p = (__be64 *)(imdp + 1); + for (i = 0; i < wr->wr.fast_reg.page_list_len; i++, p++) + *p = cpu_to_be64( + (u64)wr->wr.fast_reg.page_list->page_list[i]); + *len16 = DIV_ROUND_UP(sizeof wqe->fr + sizeof *imdp + pbllen, + 16); + } + return 0; +} + +static int build_inv_stag(union t4_wr *wqe, struct ib_send_wr *wr, + u8 *len16) +{ + wqe->inv.stag_inv = cpu_to_be32(wr->ex.invalidate_rkey); + wqe->inv.r2 = 0; + *len16 = DIV_ROUND_UP(sizeof wqe->inv, 16); + return 0; +} + +void c4iw_qp_add_ref(struct ib_qp *qp) +{ + PDBG("%s ib_qp %p\n", __func__, qp); + atomic_inc(&(to_c4iw_qp(qp)->refcnt)); +} + +void c4iw_qp_rem_ref(struct ib_qp *qp) +{ + PDBG("%s ib_qp %p\n", __func__, qp); + if (atomic_dec_and_test(&(to_c4iw_qp(qp)->refcnt))) + wake_up(&(to_c4iw_qp(qp)->wait)); +} + +int c4iw_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, + struct ib_send_wr **bad_wr) +{ + int err = 0; + u8 len16 = 0; + enum fw_wr_opcodes fw_opcode = 0; + enum fw_ri_wr_flags fw_flags; + struct c4iw_qp *qhp; + union t4_wr *wqe; + u32 num_wrs; + struct t4_swsqe *swsqe; + unsigned long flag; + u16 idx = 0; + + qhp = to_c4iw_qp(ibqp); + spin_lock_irqsave(&qhp->lock, flag); + if (t4_wq_in_error(&qhp->wq)) { + spin_unlock_irqrestore(&qhp->lock, flag); + return -EINVAL; + } + num_wrs = t4_sq_avail(&qhp->wq); + if (num_wrs == 0) { + spin_unlock_irqrestore(&qhp->lock, flag); + return -ENOMEM; + } + while (wr) { + if (num_wrs == 0) { + err = -ENOMEM; + *bad_wr = wr; + break; + } + wqe = &qhp->wq.sq.queue[qhp->wq.sq.pidx]; + fw_flags = 0; + if (wr->send_flags & IB_SEND_SOLICITED) + fw_flags |= FW_RI_SOLICITED_EVENT_FLAG; + if (wr->send_flags & IB_SEND_SIGNALED) + fw_flags |= FW_RI_COMPLETION_FLAG; + swsqe = &qhp->wq.sq.sw_sq[qhp->wq.sq.pidx]; + switch (wr->opcode) { + case IB_WR_SEND_WITH_INV: + case IB_WR_SEND: + if (wr->send_flags & IB_SEND_FENCE) + fw_flags |= FW_RI_READ_FENCE_FLAG; + fw_opcode = FW_RI_SEND_WR; + if (wr->opcode == IB_WR_SEND) + swsqe->opcode = FW_RI_SEND; + else + swsqe->opcode = FW_RI_SEND_WITH_INV; + err = build_rdma_send(wqe, wr, &len16); + break; + case IB_WR_RDMA_WRITE: + fw_opcode = FW_RI_RDMA_WRITE_WR; + swsqe->opcode = FW_RI_RDMA_WRITE; + err = build_rdma_write(wqe, wr, &len16); + break; + case IB_WR_RDMA_READ: + fw_opcode = FW_RI_RDMA_READ_WR; + swsqe->opcode = FW_RI_READ_REQ; + fw_flags = 0; + err = build_rdma_read(wqe, wr, &len16); + if (err) + break; + swsqe->read_len = wr->sg_list[0].length; + if (!qhp->wq.sq.oldest_read) + qhp->wq.sq.oldest_read = swsqe; + break; + case IB_WR_FAST_REG_MR: + fw_opcode = FW_RI_FR_NSMR_WR; + swsqe->opcode = FW_RI_FAST_REGISTER; + err = build_fastreg(wqe, wr, &len16); + break; + case IB_WR_LOCAL_INV: + fw_opcode = FW_RI_INV_LSTAG_WR; + swsqe->opcode = FW_RI_LOCAL_INV; + err = build_inv_stag(wqe, wr, &len16); + break; + default: + PDBG("%s post of type=%d TBD!\n", __func__, + wr->opcode); + err = -EINVAL; + } + if (err) { + *bad_wr = wr; + break; + } + swsqe->idx = qhp->wq.sq.pidx; + swsqe->complete = 0; + swsqe->signaled = (wr->send_flags & IB_SEND_SIGNALED); + swsqe->wr_id = wr->wr_id; + + init_wr_hdr(wqe, qhp->wq.sq.pidx, fw_opcode, fw_flags, len16); + + PDBG("%s cookie 0x%llx pidx 0x%x opcode 0x%x read_len %u\n", + __func__, (unsigned long long)wr->wr_id, qhp->wq.sq.pidx, + swsqe->opcode, swsqe->read_len); + wr = wr->next; + num_wrs--; + t4_sq_produce(&qhp->wq); + idx++; + } + if (t4_wq_db_enabled(&qhp->wq)) + t4_ring_sq_db(&qhp->wq, idx); + spin_unlock_irqrestore(&qhp->lock, flag); + return err; +} + +int c4iw_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr) +{ + int err = 0; + struct c4iw_qp *qhp; + union t4_recv_wr *wqe; + u32 num_wrs; + u8 len16 = 0; + unsigned long flag; + u16 idx = 0; + + qhp = to_c4iw_qp(ibqp); + spin_lock_irqsave(&qhp->lock, flag); + if (t4_wq_in_error(&qhp->wq)) { + spin_unlock_irqrestore(&qhp->lock, flag); + return -EINVAL; + } + num_wrs = t4_rq_avail(&qhp->wq); + if (num_wrs == 0) { + spin_unlock_irqrestore(&qhp->lock, flag); + return -ENOMEM; + } + while (wr) { + if (wr->num_sge > T4_MAX_RECV_SGE) { + err = -EINVAL; + *bad_wr = wr; + break; + } + wqe = &qhp->wq.rq.queue[qhp->wq.rq.pidx]; + if (num_wrs) + err = build_rdma_recv(qhp, wqe, wr, &len16); + else + err = -ENOMEM; + if (err) { + *bad_wr = wr; + break; + } + + qhp->wq.rq.sw_rq[qhp->wq.rq.pidx].wr_id = wr->wr_id; + + wqe->recv.opcode = FW_RI_RECV_WR; + wqe->recv.r1 = 0; + wqe->recv.wrid = qhp->wq.rq.pidx; + wqe->recv.r2[0] = 0; + wqe->recv.r2[1] = 0; + wqe->recv.r2[2] = 0; + wqe->recv.len16 = len16; + if (len16 < 5) + wqe->flits[8] = 0; + + PDBG("%s cookie 0x%llx pidx %u\n", __func__, + (unsigned long long) wr->wr_id, qhp->wq.rq.pidx); + t4_rq_produce(&qhp->wq); + wr = wr->next; + num_wrs--; + idx++; + } + if (t4_wq_db_enabled(&qhp->wq)) + t4_ring_rq_db(&qhp->wq, idx); + spin_unlock_irqrestore(&qhp->lock, flag); + return err; +} + +int c4iw_bind_mw(struct ib_qp *qp, struct ib_mw *mw, struct ib_mw_bind *mw_bind) +{ + return -ENOSYS; +} + +static inline void build_term_codes(struct t4_cqe *err_cqe, u8 *layer_type, + u8 *ecode) +{ + int status; + int tagged; + int opcode; + int rqtype; + int send_inv; + + if (!err_cqe) { + *layer_type = LAYER_RDMAP|DDP_LOCAL_CATA; + *ecode = 0; + return; + } + + status = CQE_STATUS(err_cqe); + opcode = CQE_OPCODE(err_cqe); + rqtype = RQ_TYPE(err_cqe); + send_inv = (opcode == FW_RI_SEND_WITH_INV) || + (opcode == FW_RI_SEND_WITH_SE_INV); + tagged = (opcode == FW_RI_RDMA_WRITE) || + (rqtype && (opcode == FW_RI_READ_RESP)); + + switch (status) { + case T4_ERR_STAG: + if (send_inv) { + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_OP; + *ecode = RDMAP_CANT_INV_STAG; + } else { + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_PROT; + *ecode = RDMAP_INV_STAG; + } + break; + case T4_ERR_PDID: + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_PROT; + if ((opcode == FW_RI_SEND_WITH_INV) || + (opcode == FW_RI_SEND_WITH_SE_INV)) + *ecode = RDMAP_CANT_INV_STAG; + else + *ecode = RDMAP_STAG_NOT_ASSOC; + break; + case T4_ERR_QPID: + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_PROT; + *ecode = RDMAP_STAG_NOT_ASSOC; + break; + case T4_ERR_ACCESS: + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_PROT; + *ecode = RDMAP_ACC_VIOL; + break; + case T4_ERR_WRAP: + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_PROT; + *ecode = RDMAP_TO_WRAP; + break; + case T4_ERR_BOUND: + if (tagged) { + *layer_type = LAYER_DDP|DDP_TAGGED_ERR; + *ecode = DDPT_BASE_BOUNDS; + } else { + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_PROT; + *ecode = RDMAP_BASE_BOUNDS; + } + break; + case T4_ERR_INVALIDATE_SHARED_MR: + case T4_ERR_INVALIDATE_MR_WITH_MW_BOUND: + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_OP; + *ecode = RDMAP_CANT_INV_STAG; + break; + case T4_ERR_ECC: + case T4_ERR_ECC_PSTAG: + case T4_ERR_INTERNAL_ERR: + *layer_type = LAYER_RDMAP|RDMAP_LOCAL_CATA; + *ecode = 0; + break; + case T4_ERR_OUT_OF_RQE: + *layer_type = LAYER_DDP|DDP_UNTAGGED_ERR; + *ecode = DDPU_INV_MSN_NOBUF; + break; + case T4_ERR_PBL_ADDR_BOUND: + *layer_type = LAYER_DDP|DDP_TAGGED_ERR; + *ecode = DDPT_BASE_BOUNDS; + break; + case T4_ERR_CRC: + *layer_type = LAYER_MPA|DDP_LLP; + *ecode = MPA_CRC_ERR; + break; + case T4_ERR_MARKER: + *layer_type = LAYER_MPA|DDP_LLP; + *ecode = MPA_MARKER_ERR; + break; + case T4_ERR_PDU_LEN_ERR: + *layer_type = LAYER_DDP|DDP_UNTAGGED_ERR; + *ecode = DDPU_MSG_TOOBIG; + break; + case T4_ERR_DDP_VERSION: + if (tagged) { + *layer_type = LAYER_DDP|DDP_TAGGED_ERR; + *ecode = DDPT_INV_VERS; + } else { + *layer_type = LAYER_DDP|DDP_UNTAGGED_ERR; + *ecode = DDPU_INV_VERS; + } + break; + case T4_ERR_RDMA_VERSION: + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_OP; + *ecode = RDMAP_INV_VERS; + break; + case T4_ERR_OPCODE: + *layer_type = LAYER_RDMAP|RDMAP_REMOTE_OP; + *ecode = RDMAP_INV_OPCODE; + break; + case T4_ERR_DDP_QUEUE_NUM: + *layer_type = LAYER_DDP|DDP_UNTAGGED_ERR; + *ecode = DDPU_INV_QN; + break; + case T4_ERR_MSN: + case T4_ERR_MSN_GAP: + case T4_ERR_MSN_RANGE: + case T4_ERR_IRD_OVERFLOW: + *layer_type = LAYER_DDP|DDP_UNTAGGED_ERR; + *ecode = DDPU_INV_MSN_RANGE; + break; + case T4_ERR_TBIT: + *layer_type = LAYER_DDP|DDP_LOCAL_CATA; + *ecode = 0; + break; + case T4_ERR_MO: + *layer_type = LAYER_DDP|DDP_UNTAGGED_ERR; + *ecode = DDPU_INV_MO; + break; + default: + *layer_type = LAYER_RDMAP|DDP_LOCAL_CATA; + *ecode = 0; + break; + } +} + +int c4iw_post_zb_read(struct c4iw_qp *qhp) +{ + union t4_wr *wqe; + struct sk_buff *skb; + u8 len16; + + PDBG("%s enter\n", __func__); + skb = alloc_skb(40, GFP_KERNEL); + if (!skb) { + printk(KERN_ERR "%s cannot send zb_read!!\n", __func__); + return -ENOMEM; + } + set_wr_txq(skb, CPL_PRIORITY_DATA, qhp->ep->txq_idx); + + wqe = (union t4_wr *)skb_put(skb, sizeof wqe->read); + memset(wqe, 0, sizeof wqe->read); + wqe->read.r2 = cpu_to_be64(0); + wqe->read.stag_sink = cpu_to_be32(1); + wqe->read.to_sink_hi = cpu_to_be32(0); + wqe->read.to_sink_lo = cpu_to_be32(1); + wqe->read.stag_src = cpu_to_be32(1); + wqe->read.plen = cpu_to_be32(0); + wqe->read.to_src_hi = cpu_to_be32(0); + wqe->read.to_src_lo = cpu_to_be32(1); + len16 = DIV_ROUND_UP(sizeof wqe->read, 16); + init_wr_hdr(wqe, 0, FW_RI_RDMA_READ_WR, FW_RI_COMPLETION_FLAG, len16); + + return c4iw_ofld_send(&qhp->rhp->rdev, skb); +} + +static void post_terminate(struct c4iw_qp *qhp, struct t4_cqe *err_cqe, + gfp_t gfp) +{ + struct fw_ri_wr *wqe; + struct sk_buff *skb; + struct terminate_message *term; + + PDBG("%s qhp %p qid 0x%x tid %u\n", __func__, qhp, qhp->wq.sq.qid, + qhp->ep->hwtid); + + skb = alloc_skb(sizeof *wqe, gfp); + if (!skb) + return; + set_wr_txq(skb, CPL_PRIORITY_DATA, qhp->ep->txq_idx); + + wqe = (struct fw_ri_wr *)__skb_put(skb, sizeof(*wqe)); + memset(wqe, 0, sizeof *wqe); + wqe->op_compl = cpu_to_be32(FW_WR_OP(FW_RI_INIT_WR)); + wqe->flowid_len16 = cpu_to_be32( + FW_WR_FLOWID(qhp->ep->hwtid) | + FW_WR_LEN16(DIV_ROUND_UP(sizeof *wqe, 16))); + + wqe->u.terminate.type = FW_RI_TYPE_TERMINATE; + wqe->u.terminate.immdlen = cpu_to_be32(sizeof *term); + term = (struct terminate_message *)wqe->u.terminate.termmsg; + build_term_codes(err_cqe, &term->layer_etype, &term->ecode); + c4iw_ofld_send(&qhp->rhp->rdev, skb); +} + +/* + * Assumes qhp lock is held. + */ +static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp, + struct c4iw_cq *schp, unsigned long *flag) +{ + int count; + int flushed; + + PDBG("%s qhp %p rchp %p schp %p\n", __func__, qhp, rchp, schp); + /* take a ref on the qhp since we must release the lock */ + atomic_inc(&qhp->refcnt); + spin_unlock_irqrestore(&qhp->lock, *flag); + + /* locking heirarchy: cq lock first, then qp lock. */ + spin_lock_irqsave(&rchp->lock, *flag); + spin_lock(&qhp->lock); + c4iw_flush_hw_cq(&rchp->cq); + c4iw_count_rcqes(&rchp->cq, &qhp->wq, &count); + flushed = c4iw_flush_rq(&qhp->wq, &rchp->cq, count); + spin_unlock(&qhp->lock); + spin_unlock_irqrestore(&rchp->lock, *flag); + if (flushed) + (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context); + + /* locking heirarchy: cq lock first, then qp lock. */ + spin_lock_irqsave(&schp->lock, *flag); + spin_lock(&qhp->lock); + c4iw_flush_hw_cq(&schp->cq); + c4iw_count_scqes(&schp->cq, &qhp->wq, &count); + flushed = c4iw_flush_sq(&qhp->wq, &schp->cq, count); + spin_unlock(&qhp->lock); + spin_unlock_irqrestore(&schp->lock, *flag); + if (flushed) + (*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context); + + /* deref */ + if (atomic_dec_and_test(&qhp->refcnt)) + wake_up(&qhp->wait); + + spin_lock_irqsave(&qhp->lock, *flag); +} + +static void flush_qp(struct c4iw_qp *qhp, unsigned long *flag) +{ + struct c4iw_cq *rchp, *schp; + + rchp = get_chp(qhp->rhp, qhp->attr.rcq); + schp = get_chp(qhp->rhp, qhp->attr.scq); + + if (qhp->ibqp.uobject) { + t4_set_wq_in_error(&qhp->wq); + t4_set_cq_in_error(&rchp->cq); + if (schp != rchp) + t4_set_cq_in_error(&schp->cq); + return; + } + __flush_qp(qhp, rchp, schp, flag); +} + +static int rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp) +{ + struct fw_ri_wr *wqe; + int ret; + struct c4iw_wr_wait wr_wait; + struct sk_buff *skb; + + PDBG("%s qhp %p qid 0x%x tid %u\n", __func__, qhp, qhp->wq.sq.qid, + qhp->ep->hwtid); + + skb = alloc_skb(sizeof *wqe, GFP_KERNEL | __GFP_NOFAIL); + if (!skb) + return -ENOMEM; + set_wr_txq(skb, CPL_PRIORITY_DATA, qhp->ep->txq_idx); + + wqe = (struct fw_ri_wr *)__skb_put(skb, sizeof(*wqe)); + memset(wqe, 0, sizeof *wqe); + wqe->op_compl = cpu_to_be32( + FW_WR_OP(FW_RI_INIT_WR) | + FW_WR_COMPL(1)); + wqe->flowid_len16 = cpu_to_be32( + FW_WR_FLOWID(qhp->ep->hwtid) | + FW_WR_LEN16(DIV_ROUND_UP(sizeof *wqe, 16))); + wqe->cookie = (u64)&wr_wait; + + wqe->u.fini.type = FW_RI_TYPE_FINI; + c4iw_init_wr_wait(&wr_wait); + ret = c4iw_ofld_send(&rhp->rdev, skb); + if (ret) + goto out; + + wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); + if (!wr_wait.done) { + printk(KERN_ERR MOD "Device %s not responding!\n", + pci_name(rhp->rdev.lldi.pdev)); + rhp->rdev.flags = T4_FATAL_ERROR; + ret = -EIO; + } else { + ret = wr_wait.ret; + if (ret) + printk(KERN_WARNING MOD + "%s: Abnormal close qpid %d ret %u\n", + pci_name(rhp->rdev.lldi.pdev), qhp->wq.sq.qid, + ret); + } +out: + PDBG("%s ret %d\n", __func__, ret); + return ret; +} + +static void build_rtr_msg(u8 p2p_type, struct fw_ri_init *init) +{ + memset(&init->u, 0, sizeof init->u); + switch (p2p_type) { + case FW_RI_INIT_P2PTYPE_RDMA_WRITE: + init->u.write.opcode = FW_RI_RDMA_WRITE_WR; + init->u.write.stag_sink = cpu_to_be32(1); + init->u.write.to_sink = cpu_to_be64(1); + init->u.write.u.immd_src[0].op = FW_RI_DATA_IMMD; + init->u.write.len16 = DIV_ROUND_UP(sizeof init->u.write + + sizeof(struct fw_ri_immd), + 16); + break; + case FW_RI_INIT_P2PTYPE_READ_REQ: + init->u.write.opcode = FW_RI_RDMA_READ_WR; + init->u.read.stag_src = cpu_to_be32(1); + init->u.read.to_src_lo = cpu_to_be32(1); + init->u.read.stag_sink = cpu_to_be32(1); + init->u.read.to_sink_lo = cpu_to_be32(1); + init->u.read.len16 = DIV_ROUND_UP(sizeof init->u.read, 16); + break; + } +} + +static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp) +{ + struct fw_ri_wr *wqe; + int ret; + struct c4iw_wr_wait wr_wait; + struct sk_buff *skb; + + PDBG("%s qhp %p qid 0x%x tid %u\n", __func__, qhp, qhp->wq.sq.qid, + qhp->ep->hwtid); + + skb = alloc_skb(sizeof *wqe, GFP_KERNEL | __GFP_NOFAIL); + if (!skb) + return -ENOMEM; + set_wr_txq(skb, CPL_PRIORITY_DATA, qhp->ep->txq_idx); + + wqe = (struct fw_ri_wr *)__skb_put(skb, sizeof(*wqe)); + memset(wqe, 0, sizeof *wqe); + wqe->op_compl = cpu_to_be32( + FW_WR_OP(FW_RI_INIT_WR) | + FW_WR_COMPL(1)); + wqe->flowid_len16 = cpu_to_be32( + FW_WR_FLOWID(qhp->ep->hwtid) | + FW_WR_LEN16(DIV_ROUND_UP(sizeof *wqe, 16))); + + wqe->cookie = (u64)&wr_wait; + + wqe->u.init.type = FW_RI_TYPE_INIT; + wqe->u.init.mpareqbit_p2ptype = + V_FW_RI_WR_MPAREQBIT(qhp->attr.mpa_attr.initiator) | + V_FW_RI_WR_P2PTYPE(qhp->attr.mpa_attr.p2p_type); + wqe->u.init.mpa_attrs = FW_RI_MPA_IETF_ENABLE; + if (qhp->attr.mpa_attr.recv_marker_enabled) + wqe->u.init.mpa_attrs |= FW_RI_MPA_RX_MARKER_ENABLE; + if (qhp->attr.mpa_attr.xmit_marker_enabled) + wqe->u.init.mpa_attrs |= FW_RI_MPA_TX_MARKER_ENABLE; + if (qhp->attr.mpa_attr.crc_enabled) + wqe->u.init.mpa_attrs |= FW_RI_MPA_CRC_ENABLE; + + wqe->u.init.qp_caps = FW_RI_QP_RDMA_READ_ENABLE | + FW_RI_QP_RDMA_WRITE_ENABLE | + FW_RI_QP_BIND_ENABLE; + if (!qhp->ibqp.uobject) + wqe->u.init.qp_caps |= FW_RI_QP_FAST_REGISTER_ENABLE | + FW_RI_QP_STAG0_ENABLE; + wqe->u.init.nrqe = cpu_to_be16(t4_rqes_posted(&qhp->wq)); + wqe->u.init.pdid = cpu_to_be32(qhp->attr.pd); + wqe->u.init.qpid = cpu_to_be32(qhp->wq.sq.qid); + wqe->u.init.sq_eqid = cpu_to_be32(qhp->wq.sq.qid); + wqe->u.init.rq_eqid = cpu_to_be32(qhp->wq.rq.qid); + wqe->u.init.scqid = cpu_to_be32(qhp->attr.scq); + wqe->u.init.rcqid = cpu_to_be32(qhp->attr.rcq); + wqe->u.init.ord_max = cpu_to_be32(qhp->attr.max_ord); + wqe->u.init.ird_max = cpu_to_be32(qhp->attr.max_ird); + wqe->u.init.iss = cpu_to_be32(qhp->ep->snd_seq); + wqe->u.init.irs = cpu_to_be32(qhp->ep->rcv_seq); + wqe->u.init.hwrqsize = cpu_to_be32(qhp->wq.rq.rqt_size); + wqe->u.init.hwrqaddr = cpu_to_be32(qhp->wq.rq.rqt_hwaddr - + rhp->rdev.lldi.vr->rq.start); + if (qhp->attr.mpa_attr.initiator) + build_rtr_msg(qhp->attr.mpa_attr.p2p_type, &wqe->u.init); + + c4iw_init_wr_wait(&wr_wait); + ret = c4iw_ofld_send(&rhp->rdev, skb); + if (ret) + goto out; + + wait_event_timeout(wr_wait.wait, wr_wait.done, C4IW_WR_TO); + if (!wr_wait.done) { + printk(KERN_ERR MOD "Device %s not responding!\n", + pci_name(rhp->rdev.lldi.pdev)); + rhp->rdev.flags = T4_FATAL_ERROR; + ret = -EIO; + } else + ret = wr_wait.ret; +out: + PDBG("%s ret %d\n", __func__, ret); + return ret; +} + +int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp, + enum c4iw_qp_attr_mask mask, + struct c4iw_qp_attributes *attrs, + int internal) +{ + int ret = 0; + struct c4iw_qp_attributes newattr = qhp->attr; + unsigned long flag; + int disconnect = 0; + int terminate = 0; + int abort = 0; + int free = 0; + struct c4iw_ep *ep = NULL; + + PDBG("%s qhp %p sqid 0x%x rqid 0x%x ep %p state %d -> %d\n", __func__, + qhp, qhp->wq.sq.qid, qhp->wq.rq.qid, qhp->ep, qhp->attr.state, + (mask & C4IW_QP_ATTR_NEXT_STATE) ? attrs->next_state : -1); + + spin_lock_irqsave(&qhp->lock, flag); + + /* Process attr changes if in IDLE */ + if (mask & C4IW_QP_ATTR_VALID_MODIFY) { + if (qhp->attr.state != C4IW_QP_STATE_IDLE) { + ret = -EIO; + goto out; + } + if (mask & C4IW_QP_ATTR_ENABLE_RDMA_READ) + newattr.enable_rdma_read = attrs->enable_rdma_read; + if (mask & C4IW_QP_ATTR_ENABLE_RDMA_WRITE) + newattr.enable_rdma_write = attrs->enable_rdma_write; + if (mask & C4IW_QP_ATTR_ENABLE_RDMA_BIND) + newattr.enable_bind = attrs->enable_bind; + if (mask & C4IW_QP_ATTR_MAX_ORD) { + if (attrs->max_ord > c4iw_max_read_depth) { + ret = -EINVAL; + goto out; + } + newattr.max_ord = attrs->max_ord; + } + if (mask & C4IW_QP_ATTR_MAX_IRD) { + if (attrs->max_ird > c4iw_max_read_depth) { + ret = -EINVAL; + goto out; + } + newattr.max_ird = attrs->max_ird; + } + qhp->attr = newattr; + } + + if (!(mask & C4IW_QP_ATTR_NEXT_STATE)) + goto out; + if (qhp->attr.state == attrs->next_state) + goto out; + + switch (qhp->attr.state) { + case C4IW_QP_STATE_IDLE: + switch (attrs->next_state) { + case C4IW_QP_STATE_RTS: + if (!(mask & C4IW_QP_ATTR_LLP_STREAM_HANDLE)) { + ret = -EINVAL; + goto out; + } + if (!(mask & C4IW_QP_ATTR_MPA_ATTR)) { + ret = -EINVAL; + goto out; + } + qhp->attr.mpa_attr = attrs->mpa_attr; + qhp->attr.llp_stream_handle = attrs->llp_stream_handle; + qhp->ep = qhp->attr.llp_stream_handle; + qhp->attr.state = C4IW_QP_STATE_RTS; + + /* + * Ref the endpoint here and deref when we + * disassociate the endpoint from the QP. This + * happens in CLOSING->IDLE transition or *->ERROR + * transition. + */ + c4iw_get_ep(&qhp->ep->com); + spin_unlock_irqrestore(&qhp->lock, flag); + ret = rdma_init(rhp, qhp); + spin_lock_irqsave(&qhp->lock, flag); + if (ret) + goto err; + break; + case C4IW_QP_STATE_ERROR: + qhp->attr.state = C4IW_QP_STATE_ERROR; + flush_qp(qhp, &flag); + break; + default: + ret = -EINVAL; + goto out; + } + break; + case C4IW_QP_STATE_RTS: + switch (attrs->next_state) { + case C4IW_QP_STATE_CLOSING: + BUG_ON(atomic_read(&qhp->ep->com.kref.refcount) < 2); + qhp->attr.state = C4IW_QP_STATE_CLOSING; + if (!internal) { + abort = 0; + disconnect = 1; + ep = qhp->ep; + c4iw_get_ep(&ep->com); + } + spin_unlock_irqrestore(&qhp->lock, flag); + ret = rdma_fini(rhp, qhp); + spin_lock_irqsave(&qhp->lock, flag); + if (ret) { + ep = qhp->ep; + c4iw_get_ep(&ep->com); + disconnect = abort = 1; + goto err; + } + break; + case C4IW_QP_STATE_TERMINATE: + qhp->attr.state = C4IW_QP_STATE_TERMINATE; + if (qhp->ibqp.uobject) + t4_set_wq_in_error(&qhp->wq); + ep = qhp->ep; + c4iw_get_ep(&ep->com); + terminate = 1; + disconnect = 1; + break; + case C4IW_QP_STATE_ERROR: + qhp->attr.state = C4IW_QP_STATE_ERROR; + if (!internal) { + abort = 1; + disconnect = 1; + ep = qhp->ep; + c4iw_get_ep(&ep->com); + } + goto err; + break; + default: + ret = -EINVAL; + goto out; + } + break; + case C4IW_QP_STATE_CLOSING: + if (!internal) { + ret = -EINVAL; + goto out; + } + switch (attrs->next_state) { + case C4IW_QP_STATE_IDLE: + flush_qp(qhp, &flag); + qhp->attr.state = C4IW_QP_STATE_IDLE; + qhp->attr.llp_stream_handle = NULL; + c4iw_put_ep(&qhp->ep->com); + qhp->ep = NULL; + wake_up(&qhp->wait); + break; + case C4IW_QP_STATE_ERROR: + goto err; + default: + ret = -EINVAL; + goto err; + } + break; + case C4IW_QP_STATE_ERROR: + if (attrs->next_state != C4IW_QP_STATE_IDLE) { + ret = -EINVAL; + goto out; + } + if (!t4_sq_empty(&qhp->wq) || !t4_rq_empty(&qhp->wq)) { + ret = -EINVAL; + goto out; + } + qhp->attr.state = C4IW_QP_STATE_IDLE; + break; + case C4IW_QP_STATE_TERMINATE: + if (!internal) { + ret = -EINVAL; + goto out; + } + goto err; + break; + default: + printk(KERN_ERR "%s in a bad state %d\n", + __func__, qhp->attr.state); + ret = -EINVAL; + goto err; + break; + } + goto out; +err: + PDBG("%s disassociating ep %p qpid 0x%x\n", __func__, qhp->ep, + qhp->wq.sq.qid); + + /* disassociate the LLP connection */ + qhp->attr.llp_stream_handle = NULL; + ep = qhp->ep; + qhp->ep = NULL; + qhp->attr.state = C4IW_QP_STATE_ERROR; + free = 1; + wake_up(&qhp->wait); + BUG_ON(!ep); + flush_qp(qhp, &flag); +out: + spin_unlock_irqrestore(&qhp->lock, flag); + + if (terminate) + post_terminate(qhp, NULL, internal ? GFP_ATOMIC : GFP_KERNEL); + + /* + * If disconnect is 1, then we need to initiate a disconnect + * on the EP. This can be a normal close (RTS->CLOSING) or + * an abnormal close (RTS/CLOSING->ERROR). + */ + if (disconnect) { + c4iw_ep_disconnect(ep, abort, internal ? GFP_ATOMIC : + GFP_KERNEL); + c4iw_put_ep(&ep->com); + } + + /* + * If free is 1, then we've disassociated the EP from the QP + * and we need to dereference the EP. + */ + if (free) + c4iw_put_ep(&ep->com); + + PDBG("%s exit state %d\n", __func__, qhp->attr.state); + return ret; +} + +int c4iw_destroy_qp(struct ib_qp *ib_qp) +{ + struct c4iw_dev *rhp; + struct c4iw_qp *qhp; + struct c4iw_qp_attributes attrs; + struct c4iw_ucontext *ucontext; + + qhp = to_c4iw_qp(ib_qp); + rhp = qhp->rhp; + + attrs.next_state = C4IW_QP_STATE_ERROR; + c4iw_modify_qp(rhp, qhp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 0); + wait_event(qhp->wait, !qhp->ep); + + remove_handle(rhp, &rhp->qpidr, qhp->wq.sq.qid); + remove_handle(rhp, &rhp->qpidr, qhp->wq.rq.qid); + atomic_dec(&qhp->refcnt); + wait_event(qhp->wait, !atomic_read(&qhp->refcnt)); + + ucontext = ib_qp->uobject ? + to_c4iw_ucontext(ib_qp->uobject->context) : NULL; + destroy_qp(&rhp->rdev, &qhp->wq, + ucontext ? &ucontext->uctx : &rhp->rdev.uctx); + + PDBG("%s ib_qp %p qpid 0x%0x\n", __func__, ib_qp, qhp->wq.sq.qid); + kfree(qhp); + return 0; +} + +struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, + struct ib_udata *udata) +{ + struct c4iw_dev *rhp; + struct c4iw_qp *qhp; + struct c4iw_pd *php; + struct c4iw_cq *schp; + struct c4iw_cq *rchp; + struct c4iw_create_qp_resp uresp; + int sqsize, rqsize; + struct c4iw_ucontext *ucontext; + int ret; + struct c4iw_mm_entry *mm1, *mm2, *mm3, *mm4; + + PDBG("%s ib_pd %p\n", __func__, pd); + + if (attrs->qp_type != IB_QPT_RC) + return ERR_PTR(-EINVAL); + + php = to_c4iw_pd(pd); + rhp = php->rhp; + schp = get_chp(rhp, ((struct c4iw_cq *)attrs->send_cq)->cq.cqid); + rchp = get_chp(rhp, ((struct c4iw_cq *)attrs->recv_cq)->cq.cqid); + if (!schp || !rchp) + return ERR_PTR(-EINVAL); + + if (attrs->cap.max_inline_data > T4_MAX_SEND_INLINE) + return ERR_PTR(-EINVAL); + + rqsize = roundup(attrs->cap.max_recv_wr + 1, 16); + if (rqsize > T4_MAX_RQ_SIZE) + return ERR_PTR(-E2BIG); + + sqsize = roundup(attrs->cap.max_send_wr + 1, 16); + if (sqsize > T4_MAX_SQ_SIZE) + return ERR_PTR(-E2BIG); + + ucontext = pd->uobject ? to_c4iw_ucontext(pd->uobject->context) : NULL; + + + qhp = kzalloc(sizeof(*qhp), GFP_KERNEL); + if (!qhp) + return ERR_PTR(-ENOMEM); + qhp->wq.sq.size = sqsize; + qhp->wq.sq.memsize = (sqsize + 1) * sizeof *qhp->wq.sq.queue; + qhp->wq.rq.size = rqsize; + qhp->wq.rq.memsize = (rqsize + 1) * sizeof *qhp->wq.rq.queue; + + if (ucontext) { + qhp->wq.sq.memsize = roundup(qhp->wq.sq.memsize, PAGE_SIZE); + qhp->wq.rq.memsize = roundup(qhp->wq.rq.memsize, PAGE_SIZE); + } + + PDBG("%s sqsize %u sqmemsize %zu rqsize %u rqmemsize %zu\n", + __func__, sqsize, qhp->wq.sq.memsize, rqsize, qhp->wq.rq.memsize); + + ret = create_qp(&rhp->rdev, &qhp->wq, &schp->cq, &rchp->cq, + ucontext ? &ucontext->uctx : &rhp->rdev.uctx); + if (ret) + goto err1; + + attrs->cap.max_recv_wr = rqsize - 1; + attrs->cap.max_send_wr = sqsize - 1; + attrs->cap.max_inline_data = T4_MAX_SEND_INLINE; + + qhp->rhp = rhp; + qhp->attr.pd = php->pdid; + qhp->attr.scq = ((struct c4iw_cq *) attrs->send_cq)->cq.cqid; + qhp->attr.rcq = ((struct c4iw_cq *) attrs->recv_cq)->cq.cqid; + qhp->attr.sq_num_entries = attrs->cap.max_send_wr; + qhp->attr.rq_num_entries = attrs->cap.max_recv_wr; + qhp->attr.sq_max_sges = attrs->cap.max_send_sge; + qhp->attr.sq_max_sges_rdma_write = attrs->cap.max_send_sge; + qhp->attr.rq_max_sges = attrs->cap.max_recv_sge; + qhp->attr.state = C4IW_QP_STATE_IDLE; + qhp->attr.next_state = C4IW_QP_STATE_IDLE; + qhp->attr.enable_rdma_read = 1; + qhp->attr.enable_rdma_write = 1; + qhp->attr.enable_bind = 1; + qhp->attr.max_ord = 1; + qhp->attr.max_ird = 1; + spin_lock_init(&qhp->lock); + init_waitqueue_head(&qhp->wait); + atomic_set(&qhp->refcnt, 1); + + ret = insert_handle(rhp, &rhp->qpidr, qhp, qhp->wq.sq.qid); + if (ret) + goto err2; + + ret = insert_handle(rhp, &rhp->qpidr, qhp, qhp->wq.rq.qid); + if (ret) + goto err3; + + if (udata) { + mm1 = kmalloc(sizeof *mm1, GFP_KERNEL); + if (!mm1) { + ret = -ENOMEM; + goto err4; + } + mm2 = kmalloc(sizeof *mm2, GFP_KERNEL); + if (!mm2) { + ret = -ENOMEM; + goto err5; + } + mm3 = kmalloc(sizeof *mm3, GFP_KERNEL); + if (!mm3) { + ret = -ENOMEM; + goto err6; + } + mm4 = kmalloc(sizeof *mm4, GFP_KERNEL); + if (!mm4) { + ret = -ENOMEM; + goto err7; + } + + uresp.qid_mask = rhp->rdev.qpmask; + uresp.sqid = qhp->wq.sq.qid; + uresp.sq_size = qhp->wq.sq.size; + uresp.sq_memsize = qhp->wq.sq.memsize; + uresp.rqid = qhp->wq.rq.qid; + uresp.rq_size = qhp->wq.rq.size; + uresp.rq_memsize = qhp->wq.rq.memsize; + spin_lock(&ucontext->mmap_lock); + uresp.sq_key = ucontext->key; + ucontext->key += PAGE_SIZE; + uresp.rq_key = ucontext->key; + ucontext->key += PAGE_SIZE; + uresp.sq_db_gts_key = ucontext->key; + ucontext->key += PAGE_SIZE; + uresp.rq_db_gts_key = ucontext->key; + ucontext->key += PAGE_SIZE; + spin_unlock(&ucontext->mmap_lock); + ret = ib_copy_to_udata(udata, &uresp, sizeof uresp); + if (ret) + goto err8; + mm1->key = uresp.sq_key; + mm1->addr = virt_to_phys(qhp->wq.sq.queue); + mm1->len = PAGE_ALIGN(qhp->wq.sq.memsize); + insert_mmap(ucontext, mm1); + mm2->key = uresp.rq_key; + mm2->addr = virt_to_phys(qhp->wq.rq.queue); + mm2->len = PAGE_ALIGN(qhp->wq.rq.memsize); + insert_mmap(ucontext, mm2); + mm3->key = uresp.sq_db_gts_key; + mm3->addr = qhp->wq.sq.udb; + mm3->len = PAGE_SIZE; + insert_mmap(ucontext, mm3); + mm4->key = uresp.rq_db_gts_key; + mm4->addr = qhp->wq.rq.udb; + mm4->len = PAGE_SIZE; + insert_mmap(ucontext, mm4); + } + qhp->ibqp.qp_num = qhp->wq.sq.qid; + init_timer(&(qhp->timer)); + PDBG("%s qhp %p sq_num_entries %d, rq_num_entries %d qpid 0x%0x\n", + __func__, qhp, qhp->attr.sq_num_entries, qhp->attr.rq_num_entries, + qhp->wq.sq.qid); + return &qhp->ibqp; +err8: + kfree(mm4); +err7: + kfree(mm3); +err6: + kfree(mm2); +err5: + kfree(mm1); +err4: + remove_handle(rhp, &rhp->qpidr, qhp->wq.rq.qid); +err3: + remove_handle(rhp, &rhp->qpidr, qhp->wq.sq.qid); +err2: + destroy_qp(&rhp->rdev, &qhp->wq, + ucontext ? &ucontext->uctx : &rhp->rdev.uctx); +err1: + kfree(qhp); + return ERR_PTR(ret); +} + +int c4iw_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask, struct ib_udata *udata) +{ + struct c4iw_dev *rhp; + struct c4iw_qp *qhp; + enum c4iw_qp_attr_mask mask = 0; + struct c4iw_qp_attributes attrs; + + PDBG("%s ib_qp %p\n", __func__, ibqp); + + /* iwarp does not support the RTR state */ + if ((attr_mask & IB_QP_STATE) && (attr->qp_state == IB_QPS_RTR)) + attr_mask &= ~IB_QP_STATE; + + /* Make sure we still have something left to do */ + if (!attr_mask) + return 0; + + memset(&attrs, 0, sizeof attrs); + qhp = to_c4iw_qp(ibqp); + rhp = qhp->rhp; + + attrs.next_state = c4iw_convert_state(attr->qp_state); + attrs.enable_rdma_read = (attr->qp_access_flags & + IB_ACCESS_REMOTE_READ) ? 1 : 0; + attrs.enable_rdma_write = (attr->qp_access_flags & + IB_ACCESS_REMOTE_WRITE) ? 1 : 0; + attrs.enable_bind = (attr->qp_access_flags & IB_ACCESS_MW_BIND) ? 1 : 0; + + + mask |= (attr_mask & IB_QP_STATE) ? C4IW_QP_ATTR_NEXT_STATE : 0; + mask |= (attr_mask & IB_QP_ACCESS_FLAGS) ? + (C4IW_QP_ATTR_ENABLE_RDMA_READ | + C4IW_QP_ATTR_ENABLE_RDMA_WRITE | + C4IW_QP_ATTR_ENABLE_RDMA_BIND) : 0; + + return c4iw_modify_qp(rhp, qhp, mask, &attrs, 0); +} + +struct ib_qp *c4iw_get_qp(struct ib_device *dev, int qpn) +{ + PDBG("%s ib_dev %p qpn 0x%x\n", __func__, dev, qpn); + return (struct ib_qp *)get_qhp(to_c4iw_dev(dev), qpn); +} diff --git a/drivers/infiniband/hw/cxgb4/resource.c b/drivers/infiniband/hw/cxgb4/resource.c new file mode 100644 index 000000000000..fb195d1d9015 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/resource.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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. + */ +/* Crude resource management */ +#include <linux/kernel.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/kfifo.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/genalloc.h> +#include "iw_cxgb4.h" + +#define RANDOM_SIZE 16 + +static int __c4iw_init_resource_fifo(struct kfifo *fifo, + spinlock_t *fifo_lock, + u32 nr, u32 skip_low, + u32 skip_high, + int random) +{ + u32 i, j, entry = 0, idx; + u32 random_bytes; + u32 rarray[16]; + spin_lock_init(fifo_lock); + + if (kfifo_alloc(fifo, nr * sizeof(u32), GFP_KERNEL)) + return -ENOMEM; + + for (i = 0; i < skip_low + skip_high; i++) + kfifo_in(fifo, (unsigned char *) &entry, sizeof(u32)); + if (random) { + j = 0; + random_bytes = random32(); + for (i = 0; i < RANDOM_SIZE; i++) + rarray[i] = i + skip_low; + for (i = skip_low + RANDOM_SIZE; i < nr - skip_high; i++) { + if (j >= RANDOM_SIZE) { + j = 0; + random_bytes = random32(); + } + idx = (random_bytes >> (j * 2)) & 0xF; + kfifo_in(fifo, + (unsigned char *) &rarray[idx], + sizeof(u32)); + rarray[idx] = i; + j++; + } + for (i = 0; i < RANDOM_SIZE; i++) + kfifo_in(fifo, + (unsigned char *) &rarray[i], + sizeof(u32)); + } else + for (i = skip_low; i < nr - skip_high; i++) + kfifo_in(fifo, (unsigned char *) &i, sizeof(u32)); + + for (i = 0; i < skip_low + skip_high; i++) + if (kfifo_out_locked(fifo, (unsigned char *) &entry, + sizeof(u32), fifo_lock)) + break; + return 0; +} + +static int c4iw_init_resource_fifo(struct kfifo *fifo, spinlock_t * fifo_lock, + u32 nr, u32 skip_low, u32 skip_high) +{ + return __c4iw_init_resource_fifo(fifo, fifo_lock, nr, skip_low, + skip_high, 0); +} + +static int c4iw_init_resource_fifo_random(struct kfifo *fifo, + spinlock_t *fifo_lock, + u32 nr, u32 skip_low, u32 skip_high) +{ + return __c4iw_init_resource_fifo(fifo, fifo_lock, nr, skip_low, + skip_high, 1); +} + +static int c4iw_init_qid_fifo(struct c4iw_rdev *rdev) +{ + u32 i; + + spin_lock_init(&rdev->resource.qid_fifo_lock); + + if (kfifo_alloc(&rdev->resource.qid_fifo, T4_MAX_QIDS * sizeof(u32), + GFP_KERNEL)) + return -ENOMEM; + + for (i = T4_QID_BASE; i < T4_QID_BASE + T4_MAX_QIDS; i++) + if (!(i & rdev->qpmask)) + kfifo_in(&rdev->resource.qid_fifo, + (unsigned char *) &i, sizeof(u32)); + return 0; +} + +/* nr_* must be power of 2 */ +int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, u32 nr_pdid) +{ + int err = 0; + err = c4iw_init_resource_fifo_random(&rdev->resource.tpt_fifo, + &rdev->resource.tpt_fifo_lock, + nr_tpt, 1, 0); + if (err) + goto tpt_err; + err = c4iw_init_qid_fifo(rdev); + if (err) + goto qid_err; + err = c4iw_init_resource_fifo(&rdev->resource.pdid_fifo, + &rdev->resource.pdid_fifo_lock, + nr_pdid, 1, 0); + if (err) + goto pdid_err; + return 0; +pdid_err: + kfifo_free(&rdev->resource.qid_fifo); +qid_err: + kfifo_free(&rdev->resource.tpt_fifo); +tpt_err: + return -ENOMEM; +} + +/* + * returns 0 if no resource available + */ +u32 c4iw_get_resource(struct kfifo *fifo, spinlock_t *lock) +{ + u32 entry; + if (kfifo_out_locked(fifo, (unsigned char *) &entry, sizeof(u32), lock)) + return entry; + else + return 0; +} + +void c4iw_put_resource(struct kfifo *fifo, u32 entry, spinlock_t *lock) +{ + PDBG("%s entry 0x%x\n", __func__, entry); + kfifo_in_locked(fifo, (unsigned char *) &entry, sizeof(u32), lock); +} + +u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx) +{ + struct c4iw_qid_list *entry; + u32 qid; + int i; + + mutex_lock(&uctx->lock); + if (!list_empty(&uctx->cqids)) { + entry = list_entry(uctx->cqids.next, struct c4iw_qid_list, + entry); + list_del(&entry->entry); + qid = entry->qid; + kfree(entry); + } else { + qid = c4iw_get_resource(&rdev->resource.qid_fifo, + &rdev->resource.qid_fifo_lock); + if (!qid) + goto out; + for (i = qid+1; i & rdev->qpmask; i++) { + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) + goto out; + entry->qid = i; + list_add_tail(&entry->entry, &uctx->cqids); + } + + /* + * now put the same ids on the qp list since they all + * map to the same db/gts page. + */ + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) + goto out; + entry->qid = qid; + list_add_tail(&entry->entry, &uctx->qpids); + for (i = qid+1; i & rdev->qpmask; i++) { + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) + goto out; + entry->qid = i; + list_add_tail(&entry->entry, &uctx->qpids); + } + } +out: + mutex_unlock(&uctx->lock); + PDBG("%s qid 0x%x\n", __func__, qid); + return qid; +} + +void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid, + struct c4iw_dev_ucontext *uctx) +{ + struct c4iw_qid_list *entry; + + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) + return; + PDBG("%s qid 0x%x\n", __func__, qid); + entry->qid = qid; + mutex_lock(&uctx->lock); + list_add_tail(&entry->entry, &uctx->cqids); + mutex_unlock(&uctx->lock); +} + +u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx) +{ + struct c4iw_qid_list *entry; + u32 qid; + int i; + + mutex_lock(&uctx->lock); + if (!list_empty(&uctx->qpids)) { + entry = list_entry(uctx->qpids.next, struct c4iw_qid_list, + entry); + list_del(&entry->entry); + qid = entry->qid; + kfree(entry); + } else { + qid = c4iw_get_resource(&rdev->resource.qid_fifo, + &rdev->resource.qid_fifo_lock); + if (!qid) + goto out; + for (i = qid+1; i & rdev->qpmask; i++) { + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) + goto out; + entry->qid = i; + list_add_tail(&entry->entry, &uctx->qpids); + } + + /* + * now put the same ids on the cq list since they all + * map to the same db/gts page. + */ + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) + goto out; + entry->qid = qid; + list_add_tail(&entry->entry, &uctx->cqids); + for (i = qid; i & rdev->qpmask; i++) { + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) + goto out; + entry->qid = i; + list_add_tail(&entry->entry, &uctx->cqids); + } + } +out: + mutex_unlock(&uctx->lock); + PDBG("%s qid 0x%x\n", __func__, qid); + return qid; +} + +void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid, + struct c4iw_dev_ucontext *uctx) +{ + struct c4iw_qid_list *entry; + + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) + return; + PDBG("%s qid 0x%x\n", __func__, qid); + entry->qid = qid; + mutex_lock(&uctx->lock); + list_add_tail(&entry->entry, &uctx->qpids); + mutex_unlock(&uctx->lock); +} + +void c4iw_destroy_resource(struct c4iw_resource *rscp) +{ + kfifo_free(&rscp->tpt_fifo); + kfifo_free(&rscp->qid_fifo); + kfifo_free(&rscp->pdid_fifo); +} + +/* + * PBL Memory Manager. Uses Linux generic allocator. + */ + +#define MIN_PBL_SHIFT 8 /* 256B == min PBL size (32 entries) */ + +u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size) +{ + unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size); + PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size); + return (u32)addr; +} + +void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size) +{ + PDBG("%s addr 0x%x size %d\n", __func__, addr, size); + gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size); +} + +int c4iw_pblpool_create(struct c4iw_rdev *rdev) +{ + unsigned pbl_start, pbl_chunk, pbl_top; + + rdev->pbl_pool = gen_pool_create(MIN_PBL_SHIFT, -1); + if (!rdev->pbl_pool) + return -ENOMEM; + + pbl_start = rdev->lldi.vr->pbl.start; + pbl_chunk = rdev->lldi.vr->pbl.size; + pbl_top = pbl_start + pbl_chunk; + + while (pbl_start < pbl_top) { + pbl_chunk = min(pbl_top - pbl_start + 1, pbl_chunk); + if (gen_pool_add(rdev->pbl_pool, pbl_start, pbl_chunk, -1)) { + PDBG("%s failed to add PBL chunk (%x/%x)\n", + __func__, pbl_start, pbl_chunk); + if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) { + printk(KERN_WARNING MOD + "Failed to add all PBL chunks (%x/%x)\n", + pbl_start, + pbl_top - pbl_start); + return 0; + } + pbl_chunk >>= 1; + } else { + PDBG("%s added PBL chunk (%x/%x)\n", + __func__, pbl_start, pbl_chunk); + pbl_start += pbl_chunk; + } + } + + return 0; +} + +void c4iw_pblpool_destroy(struct c4iw_rdev *rdev) +{ + gen_pool_destroy(rdev->pbl_pool); +} + +/* + * RQT Memory Manager. Uses Linux generic allocator. + */ + +#define MIN_RQT_SHIFT 10 /* 1KB == min RQT size (16 entries) */ + +u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size) +{ + unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6); + PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size << 6); + return (u32)addr; +} + +void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size) +{ + PDBG("%s addr 0x%x size %d\n", __func__, addr, size << 6); + gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6); +} + +int c4iw_rqtpool_create(struct c4iw_rdev *rdev) +{ + unsigned rqt_start, rqt_chunk, rqt_top; + + rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1); + if (!rdev->rqt_pool) + return -ENOMEM; + + rqt_start = rdev->lldi.vr->rq.start; + rqt_chunk = rdev->lldi.vr->rq.size; + rqt_top = rqt_start + rqt_chunk; + + while (rqt_start < rqt_top) { + rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk); + if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) { + PDBG("%s failed to add RQT chunk (%x/%x)\n", + __func__, rqt_start, rqt_chunk); + if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) { + printk(KERN_WARNING MOD + "Failed to add all RQT chunks (%x/%x)\n", + rqt_start, rqt_top - rqt_start); + return 0; + } + rqt_chunk >>= 1; + } else { + PDBG("%s added RQT chunk (%x/%x)\n", + __func__, rqt_start, rqt_chunk); + rqt_start += rqt_chunk; + } + } + return 0; +} + +void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev) +{ + gen_pool_destroy(rdev->rqt_pool); +} diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h new file mode 100644 index 000000000000..d0e8af352408 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/t4.h @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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 __T4_H__ +#define __T4_H__ + +#include "t4_hw.h" +#include "t4_regs.h" +#include "t4_msg.h" +#include "t4fw_ri_api.h" + +#define T4_QID_BASE 1024 +#define T4_MAX_QIDS 256 +#define T4_MAX_NUM_QP (1<<16) +#define T4_MAX_NUM_CQ (1<<15) +#define T4_MAX_NUM_PD (1<<15) +#define T4_MAX_PBL_SIZE 256 +#define T4_MAX_RQ_SIZE 1024 +#define T4_MAX_SQ_SIZE 1024 +#define T4_MAX_QP_DEPTH (T4_MAX_RQ_SIZE-1) +#define T4_MAX_CQ_DEPTH 8192 +#define T4_MAX_NUM_STAG (1<<15) +#define T4_MAX_MR_SIZE (~0ULL - 1) +#define T4_PAGESIZE_MASK 0xffff000 /* 4KB-128MB */ +#define T4_STAG_UNSET 0xffffffff +#define T4_FW_MAJ 0 +#define T4_EQ_STATUS_ENTRIES (L1_CACHE_BYTES > 64 ? 2 : 1) + +struct t4_status_page { + __be32 rsvd1; /* flit 0 - hw owns */ + __be16 rsvd2; + __be16 qid; + __be16 cidx; + __be16 pidx; + u8 qp_err; /* flit 1 - sw owns */ + u8 db_off; +}; + +#define T4_EQ_SIZE 64 + +#define T4_SQ_NUM_SLOTS 4 +#define T4_SQ_NUM_BYTES (T4_EQ_SIZE * T4_SQ_NUM_SLOTS) +#define T4_MAX_SEND_SGE ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_send_wr) - \ + sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge)) +#define T4_MAX_SEND_INLINE ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_send_wr) - \ + sizeof(struct fw_ri_immd))) +#define T4_MAX_WRITE_INLINE ((T4_SQ_NUM_BYTES - \ + sizeof(struct fw_ri_rdma_write_wr) - \ + sizeof(struct fw_ri_immd))) +#define T4_MAX_WRITE_SGE ((T4_SQ_NUM_BYTES - \ + sizeof(struct fw_ri_rdma_write_wr) - \ + sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge)) +#define T4_MAX_FR_IMMD ((T4_SQ_NUM_BYTES - sizeof(struct fw_ri_fr_nsmr_wr) - \ + sizeof(struct fw_ri_immd))) +#define T4_MAX_FR_DEPTH 255 + +#define T4_RQ_NUM_SLOTS 2 +#define T4_RQ_NUM_BYTES (T4_EQ_SIZE * T4_RQ_NUM_SLOTS) +#define T4_MAX_RECV_SGE ((T4_RQ_NUM_BYTES - sizeof(struct fw_ri_recv_wr) - \ + sizeof(struct fw_ri_isgl)) / sizeof(struct fw_ri_sge)) + +union t4_wr { + struct fw_ri_res_wr res; + struct fw_ri_wr ri; + struct fw_ri_rdma_write_wr write; + struct fw_ri_send_wr send; + struct fw_ri_rdma_read_wr read; + struct fw_ri_bind_mw_wr bind; + struct fw_ri_fr_nsmr_wr fr; + struct fw_ri_inv_lstag_wr inv; + struct t4_status_page status; + __be64 flits[T4_EQ_SIZE / sizeof(__be64) * T4_SQ_NUM_SLOTS]; +}; + +union t4_recv_wr { + struct fw_ri_recv_wr recv; + struct t4_status_page status; + __be64 flits[T4_EQ_SIZE / sizeof(__be64) * T4_RQ_NUM_SLOTS]; +}; + +static inline void init_wr_hdr(union t4_wr *wqe, u16 wrid, + enum fw_wr_opcodes opcode, u8 flags, u8 len16) +{ + int slots_used; + + wqe->send.opcode = (u8)opcode; + wqe->send.flags = flags; + wqe->send.wrid = wrid; + wqe->send.r1[0] = 0; + wqe->send.r1[1] = 0; + wqe->send.r1[2] = 0; + wqe->send.len16 = len16; + + slots_used = DIV_ROUND_UP(len16*16, T4_EQ_SIZE); + while (slots_used < T4_SQ_NUM_SLOTS) { + wqe->flits[slots_used * T4_EQ_SIZE / sizeof(__be64)] = 0; + slots_used++; + } +} + +/* CQE/AE status codes */ +#define T4_ERR_SUCCESS 0x0 +#define T4_ERR_STAG 0x1 /* STAG invalid: either the */ + /* STAG is offlimt, being 0, */ + /* or STAG_key mismatch */ +#define T4_ERR_PDID 0x2 /* PDID mismatch */ +#define T4_ERR_QPID 0x3 /* QPID mismatch */ +#define T4_ERR_ACCESS 0x4 /* Invalid access right */ +#define T4_ERR_WRAP 0x5 /* Wrap error */ +#define T4_ERR_BOUND 0x6 /* base and bounds voilation */ +#define T4_ERR_INVALIDATE_SHARED_MR 0x7 /* attempt to invalidate a */ + /* shared memory region */ +#define T4_ERR_INVALIDATE_MR_WITH_MW_BOUND 0x8 /* attempt to invalidate a */ + /* shared memory region */ +#define T4_ERR_ECC 0x9 /* ECC error detected */ +#define T4_ERR_ECC_PSTAG 0xA /* ECC error detected when */ + /* reading PSTAG for a MW */ + /* Invalidate */ +#define T4_ERR_PBL_ADDR_BOUND 0xB /* pbl addr out of bounds: */ + /* software error */ +#define T4_ERR_SWFLUSH 0xC /* SW FLUSHED */ +#define T4_ERR_CRC 0x10 /* CRC error */ +#define T4_ERR_MARKER 0x11 /* Marker error */ +#define T4_ERR_PDU_LEN_ERR 0x12 /* invalid PDU length */ +#define T4_ERR_OUT_OF_RQE 0x13 /* out of RQE */ +#define T4_ERR_DDP_VERSION 0x14 /* wrong DDP version */ +#define T4_ERR_RDMA_VERSION 0x15 /* wrong RDMA version */ +#define T4_ERR_OPCODE 0x16 /* invalid rdma opcode */ +#define T4_ERR_DDP_QUEUE_NUM 0x17 /* invalid ddp queue number */ +#define T4_ERR_MSN 0x18 /* MSN error */ +#define T4_ERR_TBIT 0x19 /* tag bit not set correctly */ +#define T4_ERR_MO 0x1A /* MO not 0 for TERMINATE */ + /* or READ_REQ */ +#define T4_ERR_MSN_GAP 0x1B +#define T4_ERR_MSN_RANGE 0x1C +#define T4_ERR_IRD_OVERFLOW 0x1D +#define T4_ERR_RQE_ADDR_BOUND 0x1E /* RQE addr out of bounds: */ + /* software error */ +#define T4_ERR_INTERNAL_ERR 0x1F /* internal error (opcode */ + /* mismatch) */ +/* + * CQE defs + */ +struct t4_cqe { + __be32 header; + __be32 len; + union { + struct { + __be32 stag; + __be32 msn; + } rcqe; + struct { + u32 nada1; + u16 nada2; + u16 cidx; + } scqe; + struct { + __be32 wrid_hi; + __be32 wrid_low; + } gen; + } u; + __be64 reserved; + __be64 bits_type_ts; +}; + +/* macros for flit 0 of the cqe */ + +#define S_CQE_QPID 12 +#define M_CQE_QPID 0xFFFFF +#define G_CQE_QPID(x) ((((x) >> S_CQE_QPID)) & M_CQE_QPID) +#define V_CQE_QPID(x) ((x)<<S_CQE_QPID) + +#define S_CQE_SWCQE 11 +#define M_CQE_SWCQE 0x1 +#define G_CQE_SWCQE(x) ((((x) >> S_CQE_SWCQE)) & M_CQE_SWCQE) +#define V_CQE_SWCQE(x) ((x)<<S_CQE_SWCQE) + +#define S_CQE_STATUS 5 +#define M_CQE_STATUS 0x1F +#define G_CQE_STATUS(x) ((((x) >> S_CQE_STATUS)) & M_CQE_STATUS) +#define V_CQE_STATUS(x) ((x)<<S_CQE_STATUS) + +#define S_CQE_TYPE 4 +#define M_CQE_TYPE 0x1 +#define G_CQE_TYPE(x) ((((x) >> S_CQE_TYPE)) & M_CQE_TYPE) +#define V_CQE_TYPE(x) ((x)<<S_CQE_TYPE) + +#define S_CQE_OPCODE 0 +#define M_CQE_OPCODE 0xF +#define G_CQE_OPCODE(x) ((((x) >> S_CQE_OPCODE)) & M_CQE_OPCODE) +#define V_CQE_OPCODE(x) ((x)<<S_CQE_OPCODE) + +#define SW_CQE(x) (G_CQE_SWCQE(be32_to_cpu((x)->header))) +#define CQE_QPID(x) (G_CQE_QPID(be32_to_cpu((x)->header))) +#define CQE_TYPE(x) (G_CQE_TYPE(be32_to_cpu((x)->header))) +#define SQ_TYPE(x) (CQE_TYPE((x))) +#define RQ_TYPE(x) (!CQE_TYPE((x))) +#define CQE_STATUS(x) (G_CQE_STATUS(be32_to_cpu((x)->header))) +#define CQE_OPCODE(x) (G_CQE_OPCODE(be32_to_cpu((x)->header))) + +#define CQE_SEND_OPCODE(x)( \ + (G_CQE_OPCODE(be32_to_cpu((x)->header)) == FW_RI_SEND) || \ + (G_CQE_OPCODE(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_SE) || \ + (G_CQE_OPCODE(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_INV) || \ + (G_CQE_OPCODE(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_SE_INV)) + +#define CQE_LEN(x) (be32_to_cpu((x)->len)) + +/* used for RQ completion processing */ +#define CQE_WRID_STAG(x) (be32_to_cpu((x)->u.rcqe.stag)) +#define CQE_WRID_MSN(x) (be32_to_cpu((x)->u.rcqe.msn)) + +/* used for SQ completion processing */ +#define CQE_WRID_SQ_IDX(x) ((x)->u.scqe.cidx) + +/* generic accessor macros */ +#define CQE_WRID_HI(x) ((x)->u.gen.wrid_hi) +#define CQE_WRID_LOW(x) ((x)->u.gen.wrid_low) + +/* macros for flit 3 of the cqe */ +#define S_CQE_GENBIT 63 +#define M_CQE_GENBIT 0x1 +#define G_CQE_GENBIT(x) (((x) >> S_CQE_GENBIT) & M_CQE_GENBIT) +#define V_CQE_GENBIT(x) ((x)<<S_CQE_GENBIT) + +#define S_CQE_OVFBIT 62 +#define M_CQE_OVFBIT 0x1 +#define G_CQE_OVFBIT(x) ((((x) >> S_CQE_OVFBIT)) & M_CQE_OVFBIT) + +#define S_CQE_IQTYPE 60 +#define M_CQE_IQTYPE 0x3 +#define G_CQE_IQTYPE(x) ((((x) >> S_CQE_IQTYPE)) & M_CQE_IQTYPE) + +#define M_CQE_TS 0x0fffffffffffffffULL +#define G_CQE_TS(x) ((x) & M_CQE_TS) + +#define CQE_OVFBIT(x) ((unsigned)G_CQE_OVFBIT(be64_to_cpu((x)->bits_type_ts))) +#define CQE_GENBIT(x) ((unsigned)G_CQE_GENBIT(be64_to_cpu((x)->bits_type_ts))) +#define CQE_TS(x) (G_CQE_TS(be64_to_cpu((x)->bits_type_ts))) + +struct t4_swsqe { + u64 wr_id; + struct t4_cqe cqe; + int read_len; + int opcode; + int complete; + int signaled; + u16 idx; +}; + +struct t4_sq { + union t4_wr *queue; + dma_addr_t dma_addr; + DECLARE_PCI_UNMAP_ADDR(mapping); + struct t4_swsqe *sw_sq; + struct t4_swsqe *oldest_read; + u64 udb; + size_t memsize; + u32 qid; + u16 in_use; + u16 size; + u16 cidx; + u16 pidx; +}; + +struct t4_swrqe { + u64 wr_id; +}; + +struct t4_rq { + union t4_recv_wr *queue; + dma_addr_t dma_addr; + DECLARE_PCI_UNMAP_ADDR(mapping); + struct t4_swrqe *sw_rq; + u64 udb; + size_t memsize; + u32 qid; + u32 msn; + u32 rqt_hwaddr; + u16 rqt_size; + u16 in_use; + u16 size; + u16 cidx; + u16 pidx; +}; + +struct t4_wq { + struct t4_sq sq; + struct t4_rq rq; + void __iomem *db; + void __iomem *gts; + struct c4iw_rdev *rdev; +}; + +static inline int t4_rqes_posted(struct t4_wq *wq) +{ + return wq->rq.in_use; +} + +static inline int t4_rq_empty(struct t4_wq *wq) +{ + return wq->rq.in_use == 0; +} + +static inline int t4_rq_full(struct t4_wq *wq) +{ + return wq->rq.in_use == (wq->rq.size - 1); +} + +static inline u32 t4_rq_avail(struct t4_wq *wq) +{ + return wq->rq.size - 1 - wq->rq.in_use; +} + +static inline void t4_rq_produce(struct t4_wq *wq) +{ + wq->rq.in_use++; + if (++wq->rq.pidx == wq->rq.size) + wq->rq.pidx = 0; +} + +static inline void t4_rq_consume(struct t4_wq *wq) +{ + wq->rq.in_use--; + wq->rq.msn++; + if (++wq->rq.cidx == wq->rq.size) + wq->rq.cidx = 0; +} + +static inline int t4_sq_empty(struct t4_wq *wq) +{ + return wq->sq.in_use == 0; +} + +static inline int t4_sq_full(struct t4_wq *wq) +{ + return wq->sq.in_use == (wq->sq.size - 1); +} + +static inline u32 t4_sq_avail(struct t4_wq *wq) +{ + return wq->sq.size - 1 - wq->sq.in_use; +} + +static inline void t4_sq_produce(struct t4_wq *wq) +{ + wq->sq.in_use++; + if (++wq->sq.pidx == wq->sq.size) + wq->sq.pidx = 0; +} + +static inline void t4_sq_consume(struct t4_wq *wq) +{ + wq->sq.in_use--; + if (++wq->sq.cidx == wq->sq.size) + wq->sq.cidx = 0; +} + +static inline void t4_ring_sq_db(struct t4_wq *wq, u16 inc) +{ + inc *= T4_SQ_NUM_SLOTS; + wmb(); + writel(QID(wq->sq.qid) | PIDX(inc), wq->db); +} + +static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc) +{ + inc *= T4_RQ_NUM_SLOTS; + wmb(); + writel(QID(wq->rq.qid) | PIDX(inc), wq->db); +} + +static inline int t4_wq_in_error(struct t4_wq *wq) +{ + return wq->sq.queue[wq->sq.size].status.qp_err; +} + +static inline void t4_set_wq_in_error(struct t4_wq *wq) +{ + wq->sq.queue[wq->sq.size].status.qp_err = 1; + wq->rq.queue[wq->rq.size].status.qp_err = 1; +} + +static inline void t4_disable_wq_db(struct t4_wq *wq) +{ + wq->sq.queue[wq->sq.size].status.db_off = 1; + wq->rq.queue[wq->rq.size].status.db_off = 1; +} + +static inline void t4_enable_wq_db(struct t4_wq *wq) +{ + wq->sq.queue[wq->sq.size].status.db_off = 0; + wq->rq.queue[wq->rq.size].status.db_off = 0; +} + +static inline int t4_wq_db_enabled(struct t4_wq *wq) +{ + return !wq->sq.queue[wq->sq.size].status.db_off; +} + +struct t4_cq { + struct t4_cqe *queue; + dma_addr_t dma_addr; + DECLARE_PCI_UNMAP_ADDR(mapping); + struct t4_cqe *sw_queue; + void __iomem *gts; + struct c4iw_rdev *rdev; + u64 ugts; + size_t memsize; + u64 timestamp; + u32 cqid; + u16 size; /* including status page */ + u16 cidx; + u16 sw_pidx; + u16 sw_cidx; + u16 sw_in_use; + u16 cidx_inc; + u8 gen; + u8 error; +}; + +static inline int t4_arm_cq(struct t4_cq *cq, int se) +{ + u32 val; + u16 inc; + + do { + /* + * inc must be less the both the max update value -and- + * the size of the CQ. + */ + inc = cq->cidx_inc <= CIDXINC_MASK ? cq->cidx_inc : + CIDXINC_MASK; + inc = inc <= (cq->size - 1) ? inc : (cq->size - 1); + if (inc == cq->cidx_inc) + val = SEINTARM(se) | CIDXINC(inc) | TIMERREG(6) | + INGRESSQID(cq->cqid); + else + val = SEINTARM(0) | CIDXINC(inc) | TIMERREG(7) | + INGRESSQID(cq->cqid); + cq->cidx_inc -= inc; + writel(val, cq->gts); + } while (cq->cidx_inc); + return 0; +} + +static inline void t4_swcq_produce(struct t4_cq *cq) +{ + cq->sw_in_use++; + if (++cq->sw_pidx == cq->size) + cq->sw_pidx = 0; +} + +static inline void t4_swcq_consume(struct t4_cq *cq) +{ + cq->sw_in_use--; + if (++cq->sw_cidx == cq->size) + cq->sw_cidx = 0; +} + +static inline void t4_hwcq_consume(struct t4_cq *cq) +{ + cq->cidx_inc++; + if (++cq->cidx == cq->size) { + cq->cidx = 0; + cq->gen ^= 1; + } +} + +static inline int t4_valid_cqe(struct t4_cq *cq, struct t4_cqe *cqe) +{ + return (CQE_GENBIT(cqe) == cq->gen); +} + +static inline int t4_next_hw_cqe(struct t4_cq *cq, struct t4_cqe **cqe) +{ + int ret = 0; + u64 bits_type_ts = be64_to_cpu(cq->queue[cq->cidx].bits_type_ts); + + if (G_CQE_GENBIT(bits_type_ts) == cq->gen) { + *cqe = &cq->queue[cq->cidx]; + cq->timestamp = G_CQE_TS(bits_type_ts); + } else if (G_CQE_TS(bits_type_ts) > cq->timestamp) + ret = -EOVERFLOW; + else + ret = -ENODATA; + if (ret == -EOVERFLOW) { + printk(KERN_ERR MOD "cq overflow cqid %u\n", cq->cqid); + cq->error = 1; + } + return ret; +} + +static inline struct t4_cqe *t4_next_sw_cqe(struct t4_cq *cq) +{ + if (cq->sw_in_use) + return &cq->sw_queue[cq->sw_cidx]; + return NULL; +} + +static inline int t4_next_cqe(struct t4_cq *cq, struct t4_cqe **cqe) +{ + int ret = 0; + + if (cq->error) + ret = -ENODATA; + else if (cq->sw_in_use) + *cqe = &cq->sw_queue[cq->sw_cidx]; + else + ret = t4_next_hw_cqe(cq, cqe); + return ret; +} + +static inline int t4_cq_in_error(struct t4_cq *cq) +{ + return ((struct t4_status_page *)&cq->queue[cq->size])->qp_err; +} + +static inline void t4_set_cq_in_error(struct t4_cq *cq) +{ + ((struct t4_status_page *)&cq->queue[cq->size])->qp_err = 1; +} +#endif diff --git a/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h b/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h new file mode 100644 index 000000000000..fc706bd07fae --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h @@ -0,0 +1,829 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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 _T4FW_RI_API_H_ +#define _T4FW_RI_API_H_ + +#include "t4fw_api.h" + +enum fw_ri_wr_opcode { + FW_RI_RDMA_WRITE = 0x0, /* IETF RDMAP v1.0 ... */ + FW_RI_READ_REQ = 0x1, + FW_RI_READ_RESP = 0x2, + FW_RI_SEND = 0x3, + FW_RI_SEND_WITH_INV = 0x4, + FW_RI_SEND_WITH_SE = 0x5, + FW_RI_SEND_WITH_SE_INV = 0x6, + FW_RI_TERMINATE = 0x7, + FW_RI_RDMA_INIT = 0x8, /* CHELSIO RI specific ... */ + FW_RI_BIND_MW = 0x9, + FW_RI_FAST_REGISTER = 0xa, + FW_RI_LOCAL_INV = 0xb, + FW_RI_QP_MODIFY = 0xc, + FW_RI_BYPASS = 0xd, + FW_RI_RECEIVE = 0xe, + + FW_RI_SGE_EC_CR_RETURN = 0xf +}; + +enum fw_ri_wr_flags { + FW_RI_COMPLETION_FLAG = 0x01, + FW_RI_NOTIFICATION_FLAG = 0x02, + FW_RI_SOLICITED_EVENT_FLAG = 0x04, + FW_RI_READ_FENCE_FLAG = 0x08, + FW_RI_LOCAL_FENCE_FLAG = 0x10, + FW_RI_RDMA_READ_INVALIDATE = 0x20 +}; + +enum fw_ri_mpa_attrs { + FW_RI_MPA_RX_MARKER_ENABLE = 0x01, + FW_RI_MPA_TX_MARKER_ENABLE = 0x02, + FW_RI_MPA_CRC_ENABLE = 0x04, + FW_RI_MPA_IETF_ENABLE = 0x08 +}; + +enum fw_ri_qp_caps { + FW_RI_QP_RDMA_READ_ENABLE = 0x01, + FW_RI_QP_RDMA_WRITE_ENABLE = 0x02, + FW_RI_QP_BIND_ENABLE = 0x04, + FW_RI_QP_FAST_REGISTER_ENABLE = 0x08, + FW_RI_QP_STAG0_ENABLE = 0x10 +}; + +enum fw_ri_addr_type { + FW_RI_ZERO_BASED_TO = 0x00, + FW_RI_VA_BASED_TO = 0x01 +}; + +enum fw_ri_mem_perms { + FW_RI_MEM_ACCESS_REM_WRITE = 0x01, + FW_RI_MEM_ACCESS_REM_READ = 0x02, + FW_RI_MEM_ACCESS_REM = 0x03, + FW_RI_MEM_ACCESS_LOCAL_WRITE = 0x04, + FW_RI_MEM_ACCESS_LOCAL_READ = 0x08, + FW_RI_MEM_ACCESS_LOCAL = 0x0C +}; + +enum fw_ri_stag_type { + FW_RI_STAG_NSMR = 0x00, + FW_RI_STAG_SMR = 0x01, + FW_RI_STAG_MW = 0x02, + FW_RI_STAG_MW_RELAXED = 0x03 +}; + +enum fw_ri_data_op { + FW_RI_DATA_IMMD = 0x81, + FW_RI_DATA_DSGL = 0x82, + FW_RI_DATA_ISGL = 0x83 +}; + +enum fw_ri_sgl_depth { + FW_RI_SGL_DEPTH_MAX_SQ = 16, + FW_RI_SGL_DEPTH_MAX_RQ = 4 +}; + +struct fw_ri_dsge_pair { + __be32 len[2]; + __be64 addr[2]; +}; + +struct fw_ri_dsgl { + __u8 op; + __u8 r1; + __be16 nsge; + __be32 len0; + __be64 addr0; +#ifndef C99_NOT_SUPPORTED + struct fw_ri_dsge_pair sge[0]; +#endif +}; + +struct fw_ri_sge { + __be32 stag; + __be32 len; + __be64 to; +}; + +struct fw_ri_isgl { + __u8 op; + __u8 r1; + __be16 nsge; + __be32 r2; +#ifndef C99_NOT_SUPPORTED + struct fw_ri_sge sge[0]; +#endif +}; + +struct fw_ri_immd { + __u8 op; + __u8 r1; + __be16 r2; + __be32 immdlen; +#ifndef C99_NOT_SUPPORTED + __u8 data[0]; +#endif +}; + +struct fw_ri_tpte { + __be32 valid_to_pdid; + __be32 locread_to_qpid; + __be32 nosnoop_pbladdr; + __be32 len_lo; + __be32 va_hi; + __be32 va_lo_fbo; + __be32 dca_mwbcnt_pstag; + __be32 len_hi; +}; + +#define S_FW_RI_TPTE_VALID 31 +#define M_FW_RI_TPTE_VALID 0x1 +#define V_FW_RI_TPTE_VALID(x) ((x) << S_FW_RI_TPTE_VALID) +#define G_FW_RI_TPTE_VALID(x) \ + (((x) >> S_FW_RI_TPTE_VALID) & M_FW_RI_TPTE_VALID) +#define F_FW_RI_TPTE_VALID V_FW_RI_TPTE_VALID(1U) + +#define S_FW_RI_TPTE_STAGKEY 23 +#define M_FW_RI_TPTE_STAGKEY 0xff +#define V_FW_RI_TPTE_STAGKEY(x) ((x) << S_FW_RI_TPTE_STAGKEY) +#define G_FW_RI_TPTE_STAGKEY(x) \ + (((x) >> S_FW_RI_TPTE_STAGKEY) & M_FW_RI_TPTE_STAGKEY) + +#define S_FW_RI_TPTE_STAGSTATE 22 +#define M_FW_RI_TPTE_STAGSTATE 0x1 +#define V_FW_RI_TPTE_STAGSTATE(x) ((x) << S_FW_RI_TPTE_STAGSTATE) +#define G_FW_RI_TPTE_STAGSTATE(x) \ + (((x) >> S_FW_RI_TPTE_STAGSTATE) & M_FW_RI_TPTE_STAGSTATE) +#define F_FW_RI_TPTE_STAGSTATE V_FW_RI_TPTE_STAGSTATE(1U) + +#define S_FW_RI_TPTE_STAGTYPE 20 +#define M_FW_RI_TPTE_STAGTYPE 0x3 +#define V_FW_RI_TPTE_STAGTYPE(x) ((x) << S_FW_RI_TPTE_STAGTYPE) +#define G_FW_RI_TPTE_STAGTYPE(x) \ + (((x) >> S_FW_RI_TPTE_STAGTYPE) & M_FW_RI_TPTE_STAGTYPE) + +#define S_FW_RI_TPTE_PDID 0 +#define M_FW_RI_TPTE_PDID 0xfffff +#define V_FW_RI_TPTE_PDID(x) ((x) << S_FW_RI_TPTE_PDID) +#define G_FW_RI_TPTE_PDID(x) \ + (((x) >> S_FW_RI_TPTE_PDID) & M_FW_RI_TPTE_PDID) + +#define S_FW_RI_TPTE_PERM 28 +#define M_FW_RI_TPTE_PERM 0xf +#define V_FW_RI_TPTE_PERM(x) ((x) << S_FW_RI_TPTE_PERM) +#define G_FW_RI_TPTE_PERM(x) \ + (((x) >> S_FW_RI_TPTE_PERM) & M_FW_RI_TPTE_PERM) + +#define S_FW_RI_TPTE_REMINVDIS 27 +#define M_FW_RI_TPTE_REMINVDIS 0x1 +#define V_FW_RI_TPTE_REMINVDIS(x) ((x) << S_FW_RI_TPTE_REMINVDIS) +#define G_FW_RI_TPTE_REMINVDIS(x) \ + (((x) >> S_FW_RI_TPTE_REMINVDIS) & M_FW_RI_TPTE_REMINVDIS) +#define F_FW_RI_TPTE_REMINVDIS V_FW_RI_TPTE_REMINVDIS(1U) + +#define S_FW_RI_TPTE_ADDRTYPE 26 +#define M_FW_RI_TPTE_ADDRTYPE 1 +#define V_FW_RI_TPTE_ADDRTYPE(x) ((x) << S_FW_RI_TPTE_ADDRTYPE) +#define G_FW_RI_TPTE_ADDRTYPE(x) \ + (((x) >> S_FW_RI_TPTE_ADDRTYPE) & M_FW_RI_TPTE_ADDRTYPE) +#define F_FW_RI_TPTE_ADDRTYPE V_FW_RI_TPTE_ADDRTYPE(1U) + +#define S_FW_RI_TPTE_MWBINDEN 25 +#define M_FW_RI_TPTE_MWBINDEN 0x1 +#define V_FW_RI_TPTE_MWBINDEN(x) ((x) << S_FW_RI_TPTE_MWBINDEN) +#define G_FW_RI_TPTE_MWBINDEN(x) \ + (((x) >> S_FW_RI_TPTE_MWBINDEN) & M_FW_RI_TPTE_MWBINDEN) +#define F_FW_RI_TPTE_MWBINDEN V_FW_RI_TPTE_MWBINDEN(1U) + +#define S_FW_RI_TPTE_PS 20 +#define M_FW_RI_TPTE_PS 0x1f +#define V_FW_RI_TPTE_PS(x) ((x) << S_FW_RI_TPTE_PS) +#define G_FW_RI_TPTE_PS(x) \ + (((x) >> S_FW_RI_TPTE_PS) & M_FW_RI_TPTE_PS) + +#define S_FW_RI_TPTE_QPID 0 +#define M_FW_RI_TPTE_QPID 0xfffff +#define V_FW_RI_TPTE_QPID(x) ((x) << S_FW_RI_TPTE_QPID) +#define G_FW_RI_TPTE_QPID(x) \ + (((x) >> S_FW_RI_TPTE_QPID) & M_FW_RI_TPTE_QPID) + +#define S_FW_RI_TPTE_NOSNOOP 30 +#define M_FW_RI_TPTE_NOSNOOP 0x1 +#define V_FW_RI_TPTE_NOSNOOP(x) ((x) << S_FW_RI_TPTE_NOSNOOP) +#define G_FW_RI_TPTE_NOSNOOP(x) \ + (((x) >> S_FW_RI_TPTE_NOSNOOP) & M_FW_RI_TPTE_NOSNOOP) +#define F_FW_RI_TPTE_NOSNOOP V_FW_RI_TPTE_NOSNOOP(1U) + +#define S_FW_RI_TPTE_PBLADDR 0 +#define M_FW_RI_TPTE_PBLADDR 0x1fffffff +#define V_FW_RI_TPTE_PBLADDR(x) ((x) << S_FW_RI_TPTE_PBLADDR) +#define G_FW_RI_TPTE_PBLADDR(x) \ + (((x) >> S_FW_RI_TPTE_PBLADDR) & M_FW_RI_TPTE_PBLADDR) + +#define S_FW_RI_TPTE_DCA 24 +#define M_FW_RI_TPTE_DCA 0x1f +#define V_FW_RI_TPTE_DCA(x) ((x) << S_FW_RI_TPTE_DCA) +#define G_FW_RI_TPTE_DCA(x) \ + (((x) >> S_FW_RI_TPTE_DCA) & M_FW_RI_TPTE_DCA) + +#define S_FW_RI_TPTE_MWBCNT_PSTAG 0 +#define M_FW_RI_TPTE_MWBCNT_PSTAG 0xffffff +#define V_FW_RI_TPTE_MWBCNT_PSTAT(x) \ + ((x) << S_FW_RI_TPTE_MWBCNT_PSTAG) +#define G_FW_RI_TPTE_MWBCNT_PSTAG(x) \ + (((x) >> S_FW_RI_TPTE_MWBCNT_PSTAG) & M_FW_RI_TPTE_MWBCNT_PSTAG) + +enum fw_ri_res_type { + FW_RI_RES_TYPE_SQ, + FW_RI_RES_TYPE_RQ, + FW_RI_RES_TYPE_CQ, +}; + +enum fw_ri_res_op { + FW_RI_RES_OP_WRITE, + FW_RI_RES_OP_RESET, +}; + +struct fw_ri_res { + union fw_ri_restype { + struct fw_ri_res_sqrq { + __u8 restype; + __u8 op; + __be16 r3; + __be32 eqid; + __be32 r4[2]; + __be32 fetchszm_to_iqid; + __be32 dcaen_to_eqsize; + __be64 eqaddr; + } sqrq; + struct fw_ri_res_cq { + __u8 restype; + __u8 op; + __be16 r3; + __be32 iqid; + __be32 r4[2]; + __be32 iqandst_to_iqandstindex; + __be16 iqdroprss_to_iqesize; + __be16 iqsize; + __be64 iqaddr; + __be32 iqns_iqro; + __be32 r6_lo; + __be64 r7; + } cq; + } u; +}; + +struct fw_ri_res_wr { + __be32 op_nres; + __be32 len16_pkd; + __u64 cookie; +#ifndef C99_NOT_SUPPORTED + struct fw_ri_res res[0]; +#endif +}; + +#define S_FW_RI_RES_WR_NRES 0 +#define M_FW_RI_RES_WR_NRES 0xff +#define V_FW_RI_RES_WR_NRES(x) ((x) << S_FW_RI_RES_WR_NRES) +#define G_FW_RI_RES_WR_NRES(x) \ + (((x) >> S_FW_RI_RES_WR_NRES) & M_FW_RI_RES_WR_NRES) + +#define S_FW_RI_RES_WR_FETCHSZM 26 +#define M_FW_RI_RES_WR_FETCHSZM 0x1 +#define V_FW_RI_RES_WR_FETCHSZM(x) ((x) << S_FW_RI_RES_WR_FETCHSZM) +#define G_FW_RI_RES_WR_FETCHSZM(x) \ + (((x) >> S_FW_RI_RES_WR_FETCHSZM) & M_FW_RI_RES_WR_FETCHSZM) +#define F_FW_RI_RES_WR_FETCHSZM V_FW_RI_RES_WR_FETCHSZM(1U) + +#define S_FW_RI_RES_WR_STATUSPGNS 25 +#define M_FW_RI_RES_WR_STATUSPGNS 0x1 +#define V_FW_RI_RES_WR_STATUSPGNS(x) ((x) << S_FW_RI_RES_WR_STATUSPGNS) +#define G_FW_RI_RES_WR_STATUSPGNS(x) \ + (((x) >> S_FW_RI_RES_WR_STATUSPGNS) & M_FW_RI_RES_WR_STATUSPGNS) +#define F_FW_RI_RES_WR_STATUSPGNS V_FW_RI_RES_WR_STATUSPGNS(1U) + +#define S_FW_RI_RES_WR_STATUSPGRO 24 +#define M_FW_RI_RES_WR_STATUSPGRO 0x1 +#define V_FW_RI_RES_WR_STATUSPGRO(x) ((x) << S_FW_RI_RES_WR_STATUSPGRO) +#define G_FW_RI_RES_WR_STATUSPGRO(x) \ + (((x) >> S_FW_RI_RES_WR_STATUSPGRO) & M_FW_RI_RES_WR_STATUSPGRO) +#define F_FW_RI_RES_WR_STATUSPGRO V_FW_RI_RES_WR_STATUSPGRO(1U) + +#define S_FW_RI_RES_WR_FETCHNS 23 +#define M_FW_RI_RES_WR_FETCHNS 0x1 +#define V_FW_RI_RES_WR_FETCHNS(x) ((x) << S_FW_RI_RES_WR_FETCHNS) +#define G_FW_RI_RES_WR_FETCHNS(x) \ + (((x) >> S_FW_RI_RES_WR_FETCHNS) & M_FW_RI_RES_WR_FETCHNS) +#define F_FW_RI_RES_WR_FETCHNS V_FW_RI_RES_WR_FETCHNS(1U) + +#define S_FW_RI_RES_WR_FETCHRO 22 +#define M_FW_RI_RES_WR_FETCHRO 0x1 +#define V_FW_RI_RES_WR_FETCHRO(x) ((x) << S_FW_RI_RES_WR_FETCHRO) +#define G_FW_RI_RES_WR_FETCHRO(x) \ + (((x) >> S_FW_RI_RES_WR_FETCHRO) & M_FW_RI_RES_WR_FETCHRO) +#define F_FW_RI_RES_WR_FETCHRO V_FW_RI_RES_WR_FETCHRO(1U) + +#define S_FW_RI_RES_WR_HOSTFCMODE 20 +#define M_FW_RI_RES_WR_HOSTFCMODE 0x3 +#define V_FW_RI_RES_WR_HOSTFCMODE(x) ((x) << S_FW_RI_RES_WR_HOSTFCMODE) +#define G_FW_RI_RES_WR_HOSTFCMODE(x) \ + (((x) >> S_FW_RI_RES_WR_HOSTFCMODE) & M_FW_RI_RES_WR_HOSTFCMODE) + +#define S_FW_RI_RES_WR_CPRIO 19 +#define M_FW_RI_RES_WR_CPRIO 0x1 +#define V_FW_RI_RES_WR_CPRIO(x) ((x) << S_FW_RI_RES_WR_CPRIO) +#define G_FW_RI_RES_WR_CPRIO(x) \ + (((x) >> S_FW_RI_RES_WR_CPRIO) & M_FW_RI_RES_WR_CPRIO) +#define F_FW_RI_RES_WR_CPRIO V_FW_RI_RES_WR_CPRIO(1U) + +#define S_FW_RI_RES_WR_ONCHIP 18 +#define M_FW_RI_RES_WR_ONCHIP 0x1 +#define V_FW_RI_RES_WR_ONCHIP(x) ((x) << S_FW_RI_RES_WR_ONCHIP) +#define G_FW_RI_RES_WR_ONCHIP(x) \ + (((x) >> S_FW_RI_RES_WR_ONCHIP) & M_FW_RI_RES_WR_ONCHIP) +#define F_FW_RI_RES_WR_ONCHIP V_FW_RI_RES_WR_ONCHIP(1U) + +#define S_FW_RI_RES_WR_PCIECHN 16 +#define M_FW_RI_RES_WR_PCIECHN 0x3 +#define V_FW_RI_RES_WR_PCIECHN(x) ((x) << S_FW_RI_RES_WR_PCIECHN) +#define G_FW_RI_RES_WR_PCIECHN(x) \ + (((x) >> S_FW_RI_RES_WR_PCIECHN) & M_FW_RI_RES_WR_PCIECHN) + +#define S_FW_RI_RES_WR_IQID 0 +#define M_FW_RI_RES_WR_IQID 0xffff +#define V_FW_RI_RES_WR_IQID(x) ((x) << S_FW_RI_RES_WR_IQID) +#define G_FW_RI_RES_WR_IQID(x) \ + (((x) >> S_FW_RI_RES_WR_IQID) & M_FW_RI_RES_WR_IQID) + +#define S_FW_RI_RES_WR_DCAEN 31 +#define M_FW_RI_RES_WR_DCAEN 0x1 +#define V_FW_RI_RES_WR_DCAEN(x) ((x) << S_FW_RI_RES_WR_DCAEN) +#define G_FW_RI_RES_WR_DCAEN(x) \ + (((x) >> S_FW_RI_RES_WR_DCAEN) & M_FW_RI_RES_WR_DCAEN) +#define F_FW_RI_RES_WR_DCAEN V_FW_RI_RES_WR_DCAEN(1U) + +#define S_FW_RI_RES_WR_DCACPU 26 +#define M_FW_RI_RES_WR_DCACPU 0x1f +#define V_FW_RI_RES_WR_DCACPU(x) ((x) << S_FW_RI_RES_WR_DCACPU) +#define G_FW_RI_RES_WR_DCACPU(x) \ + (((x) >> S_FW_RI_RES_WR_DCACPU) & M_FW_RI_RES_WR_DCACPU) + +#define S_FW_RI_RES_WR_FBMIN 23 +#define M_FW_RI_RES_WR_FBMIN 0x7 +#define V_FW_RI_RES_WR_FBMIN(x) ((x) << S_FW_RI_RES_WR_FBMIN) +#define G_FW_RI_RES_WR_FBMIN(x) \ + (((x) >> S_FW_RI_RES_WR_FBMIN) & M_FW_RI_RES_WR_FBMIN) + +#define S_FW_RI_RES_WR_FBMAX 20 +#define M_FW_RI_RES_WR_FBMAX 0x7 +#define V_FW_RI_RES_WR_FBMAX(x) ((x) << S_FW_RI_RES_WR_FBMAX) +#define G_FW_RI_RES_WR_FBMAX(x) \ + (((x) >> S_FW_RI_RES_WR_FBMAX) & M_FW_RI_RES_WR_FBMAX) + +#define S_FW_RI_RES_WR_CIDXFTHRESHO 19 +#define M_FW_RI_RES_WR_CIDXFTHRESHO 0x1 +#define V_FW_RI_RES_WR_CIDXFTHRESHO(x) ((x) << S_FW_RI_RES_WR_CIDXFTHRESHO) +#define G_FW_RI_RES_WR_CIDXFTHRESHO(x) \ + (((x) >> S_FW_RI_RES_WR_CIDXFTHRESHO) & M_FW_RI_RES_WR_CIDXFTHRESHO) +#define F_FW_RI_RES_WR_CIDXFTHRESHO V_FW_RI_RES_WR_CIDXFTHRESHO(1U) + +#define S_FW_RI_RES_WR_CIDXFTHRESH 16 +#define M_FW_RI_RES_WR_CIDXFTHRESH 0x7 +#define V_FW_RI_RES_WR_CIDXFTHRESH(x) ((x) << S_FW_RI_RES_WR_CIDXFTHRESH) +#define G_FW_RI_RES_WR_CIDXFTHRESH(x) \ + (((x) >> S_FW_RI_RES_WR_CIDXFTHRESH) & M_FW_RI_RES_WR_CIDXFTHRESH) + +#define S_FW_RI_RES_WR_EQSIZE 0 +#define M_FW_RI_RES_WR_EQSIZE 0xffff +#define V_FW_RI_RES_WR_EQSIZE(x) ((x) << S_FW_RI_RES_WR_EQSIZE) +#define G_FW_RI_RES_WR_EQSIZE(x) \ + (((x) >> S_FW_RI_RES_WR_EQSIZE) & M_FW_RI_RES_WR_EQSIZE) + +#define S_FW_RI_RES_WR_IQANDST 15 +#define M_FW_RI_RES_WR_IQANDST 0x1 +#define V_FW_RI_RES_WR_IQANDST(x) ((x) << S_FW_RI_RES_WR_IQANDST) +#define G_FW_RI_RES_WR_IQANDST(x) \ + (((x) >> S_FW_RI_RES_WR_IQANDST) & M_FW_RI_RES_WR_IQANDST) +#define F_FW_RI_RES_WR_IQANDST V_FW_RI_RES_WR_IQANDST(1U) + +#define S_FW_RI_RES_WR_IQANUS 14 +#define M_FW_RI_RES_WR_IQANUS 0x1 +#define V_FW_RI_RES_WR_IQANUS(x) ((x) << S_FW_RI_RES_WR_IQANUS) +#define G_FW_RI_RES_WR_IQANUS(x) \ + (((x) >> S_FW_RI_RES_WR_IQANUS) & M_FW_RI_RES_WR_IQANUS) +#define F_FW_RI_RES_WR_IQANUS V_FW_RI_RES_WR_IQANUS(1U) + +#define S_FW_RI_RES_WR_IQANUD 12 +#define M_FW_RI_RES_WR_IQANUD 0x3 +#define V_FW_RI_RES_WR_IQANUD(x) ((x) << S_FW_RI_RES_WR_IQANUD) +#define G_FW_RI_RES_WR_IQANUD(x) \ + (((x) >> S_FW_RI_RES_WR_IQANUD) & M_FW_RI_RES_WR_IQANUD) + +#define S_FW_RI_RES_WR_IQANDSTINDEX 0 +#define M_FW_RI_RES_WR_IQANDSTINDEX 0xfff +#define V_FW_RI_RES_WR_IQANDSTINDEX(x) ((x) << S_FW_RI_RES_WR_IQANDSTINDEX) +#define G_FW_RI_RES_WR_IQANDSTINDEX(x) \ + (((x) >> S_FW_RI_RES_WR_IQANDSTINDEX) & M_FW_RI_RES_WR_IQANDSTINDEX) + +#define S_FW_RI_RES_WR_IQDROPRSS 15 +#define M_FW_RI_RES_WR_IQDROPRSS 0x1 +#define V_FW_RI_RES_WR_IQDROPRSS(x) ((x) << S_FW_RI_RES_WR_IQDROPRSS) +#define G_FW_RI_RES_WR_IQDROPRSS(x) \ + (((x) >> S_FW_RI_RES_WR_IQDROPRSS) & M_FW_RI_RES_WR_IQDROPRSS) +#define F_FW_RI_RES_WR_IQDROPRSS V_FW_RI_RES_WR_IQDROPRSS(1U) + +#define S_FW_RI_RES_WR_IQGTSMODE 14 +#define M_FW_RI_RES_WR_IQGTSMODE 0x1 +#define V_FW_RI_RES_WR_IQGTSMODE(x) ((x) << S_FW_RI_RES_WR_IQGTSMODE) +#define G_FW_RI_RES_WR_IQGTSMODE(x) \ + (((x) >> S_FW_RI_RES_WR_IQGTSMODE) & M_FW_RI_RES_WR_IQGTSMODE) +#define F_FW_RI_RES_WR_IQGTSMODE V_FW_RI_RES_WR_IQGTSMODE(1U) + +#define S_FW_RI_RES_WR_IQPCIECH 12 +#define M_FW_RI_RES_WR_IQPCIECH 0x3 +#define V_FW_RI_RES_WR_IQPCIECH(x) ((x) << S_FW_RI_RES_WR_IQPCIECH) +#define G_FW_RI_RES_WR_IQPCIECH(x) \ + (((x) >> S_FW_RI_RES_WR_IQPCIECH) & M_FW_RI_RES_WR_IQPCIECH) + +#define S_FW_RI_RES_WR_IQDCAEN 11 +#define M_FW_RI_RES_WR_IQDCAEN 0x1 +#define V_FW_RI_RES_WR_IQDCAEN(x) ((x) << S_FW_RI_RES_WR_IQDCAEN) +#define G_FW_RI_RES_WR_IQDCAEN(x) \ + (((x) >> S_FW_RI_RES_WR_IQDCAEN) & M_FW_RI_RES_WR_IQDCAEN) +#define F_FW_RI_RES_WR_IQDCAEN V_FW_RI_RES_WR_IQDCAEN(1U) + +#define S_FW_RI_RES_WR_IQDCACPU 6 +#define M_FW_RI_RES_WR_IQDCACPU 0x1f +#define V_FW_RI_RES_WR_IQDCACPU(x) ((x) << S_FW_RI_RES_WR_IQDCACPU) +#define G_FW_RI_RES_WR_IQDCACPU(x) \ + (((x) >> S_FW_RI_RES_WR_IQDCACPU) & M_FW_RI_RES_WR_IQDCACPU) + +#define S_FW_RI_RES_WR_IQINTCNTTHRESH 4 +#define M_FW_RI_RES_WR_IQINTCNTTHRESH 0x3 +#define V_FW_RI_RES_WR_IQINTCNTTHRESH(x) \ + ((x) << S_FW_RI_RES_WR_IQINTCNTTHRESH) +#define G_FW_RI_RES_WR_IQINTCNTTHRESH(x) \ + (((x) >> S_FW_RI_RES_WR_IQINTCNTTHRESH) & M_FW_RI_RES_WR_IQINTCNTTHRESH) + +#define S_FW_RI_RES_WR_IQO 3 +#define M_FW_RI_RES_WR_IQO 0x1 +#define V_FW_RI_RES_WR_IQO(x) ((x) << S_FW_RI_RES_WR_IQO) +#define G_FW_RI_RES_WR_IQO(x) \ + (((x) >> S_FW_RI_RES_WR_IQO) & M_FW_RI_RES_WR_IQO) +#define F_FW_RI_RES_WR_IQO V_FW_RI_RES_WR_IQO(1U) + +#define S_FW_RI_RES_WR_IQCPRIO 2 +#define M_FW_RI_RES_WR_IQCPRIO 0x1 +#define V_FW_RI_RES_WR_IQCPRIO(x) ((x) << S_FW_RI_RES_WR_IQCPRIO) +#define G_FW_RI_RES_WR_IQCPRIO(x) \ + (((x) >> S_FW_RI_RES_WR_IQCPRIO) & M_FW_RI_RES_WR_IQCPRIO) +#define F_FW_RI_RES_WR_IQCPRIO V_FW_RI_RES_WR_IQCPRIO(1U) + +#define S_FW_RI_RES_WR_IQESIZE 0 +#define M_FW_RI_RES_WR_IQESIZE 0x3 +#define V_FW_RI_RES_WR_IQESIZE(x) ((x) << S_FW_RI_RES_WR_IQESIZE) +#define G_FW_RI_RES_WR_IQESIZE(x) \ + (((x) >> S_FW_RI_RES_WR_IQESIZE) & M_FW_RI_RES_WR_IQESIZE) + +#define S_FW_RI_RES_WR_IQNS 31 +#define M_FW_RI_RES_WR_IQNS 0x1 +#define V_FW_RI_RES_WR_IQNS(x) ((x) << S_FW_RI_RES_WR_IQNS) +#define G_FW_RI_RES_WR_IQNS(x) \ + (((x) >> S_FW_RI_RES_WR_IQNS) & M_FW_RI_RES_WR_IQNS) +#define F_FW_RI_RES_WR_IQNS V_FW_RI_RES_WR_IQNS(1U) + +#define S_FW_RI_RES_WR_IQRO 30 +#define M_FW_RI_RES_WR_IQRO 0x1 +#define V_FW_RI_RES_WR_IQRO(x) ((x) << S_FW_RI_RES_WR_IQRO) +#define G_FW_RI_RES_WR_IQRO(x) \ + (((x) >> S_FW_RI_RES_WR_IQRO) & M_FW_RI_RES_WR_IQRO) +#define F_FW_RI_RES_WR_IQRO V_FW_RI_RES_WR_IQRO(1U) + +struct fw_ri_rdma_write_wr { + __u8 opcode; + __u8 flags; + __u16 wrid; + __u8 r1[3]; + __u8 len16; + __be64 r2; + __be32 plen; + __be32 stag_sink; + __be64 to_sink; +#ifndef C99_NOT_SUPPORTED + union { + struct fw_ri_immd immd_src[0]; + struct fw_ri_isgl isgl_src[0]; + } u; +#endif +}; + +struct fw_ri_send_wr { + __u8 opcode; + __u8 flags; + __u16 wrid; + __u8 r1[3]; + __u8 len16; + __be32 sendop_pkd; + __be32 stag_inv; + __be32 plen; + __be32 r3; + __be64 r4; +#ifndef C99_NOT_SUPPORTED + union { + struct fw_ri_immd immd_src[0]; + struct fw_ri_isgl isgl_src[0]; + } u; +#endif +}; + +#define S_FW_RI_SEND_WR_SENDOP 0 +#define M_FW_RI_SEND_WR_SENDOP 0xf +#define V_FW_RI_SEND_WR_SENDOP(x) ((x) << S_FW_RI_SEND_WR_SENDOP) +#define G_FW_RI_SEND_WR_SENDOP(x) \ + (((x) >> S_FW_RI_SEND_WR_SENDOP) & M_FW_RI_SEND_WR_SENDOP) + +struct fw_ri_rdma_read_wr { + __u8 opcode; + __u8 flags; + __u16 wrid; + __u8 r1[3]; + __u8 len16; + __be64 r2; + __be32 stag_sink; + __be32 to_sink_hi; + __be32 to_sink_lo; + __be32 plen; + __be32 stag_src; + __be32 to_src_hi; + __be32 to_src_lo; + __be32 r5; +}; + +struct fw_ri_recv_wr { + __u8 opcode; + __u8 r1; + __u16 wrid; + __u8 r2[3]; + __u8 len16; + struct fw_ri_isgl isgl; +}; + +struct fw_ri_bind_mw_wr { + __u8 opcode; + __u8 flags; + __u16 wrid; + __u8 r1[3]; + __u8 len16; + __u8 qpbinde_to_dcacpu; + __u8 pgsz_shift; + __u8 addr_type; + __u8 mem_perms; + __be32 stag_mr; + __be32 stag_mw; + __be32 r3; + __be64 len_mw; + __be64 va_fbo; + __be64 r4; +}; + +#define S_FW_RI_BIND_MW_WR_QPBINDE 6 +#define M_FW_RI_BIND_MW_WR_QPBINDE 0x1 +#define V_FW_RI_BIND_MW_WR_QPBINDE(x) ((x) << S_FW_RI_BIND_MW_WR_QPBINDE) +#define G_FW_RI_BIND_MW_WR_QPBINDE(x) \ + (((x) >> S_FW_RI_BIND_MW_WR_QPBINDE) & M_FW_RI_BIND_MW_WR_QPBINDE) +#define F_FW_RI_BIND_MW_WR_QPBINDE V_FW_RI_BIND_MW_WR_QPBINDE(1U) + +#define S_FW_RI_BIND_MW_WR_NS 5 +#define M_FW_RI_BIND_MW_WR_NS 0x1 +#define V_FW_RI_BIND_MW_WR_NS(x) ((x) << S_FW_RI_BIND_MW_WR_NS) +#define G_FW_RI_BIND_MW_WR_NS(x) \ + (((x) >> S_FW_RI_BIND_MW_WR_NS) & M_FW_RI_BIND_MW_WR_NS) +#define F_FW_RI_BIND_MW_WR_NS V_FW_RI_BIND_MW_WR_NS(1U) + +#define S_FW_RI_BIND_MW_WR_DCACPU 0 +#define M_FW_RI_BIND_MW_WR_DCACPU 0x1f +#define V_FW_RI_BIND_MW_WR_DCACPU(x) ((x) << S_FW_RI_BIND_MW_WR_DCACPU) +#define G_FW_RI_BIND_MW_WR_DCACPU(x) \ + (((x) >> S_FW_RI_BIND_MW_WR_DCACPU) & M_FW_RI_BIND_MW_WR_DCACPU) + +struct fw_ri_fr_nsmr_wr { + __u8 opcode; + __u8 flags; + __u16 wrid; + __u8 r1[3]; + __u8 len16; + __u8 qpbinde_to_dcacpu; + __u8 pgsz_shift; + __u8 addr_type; + __u8 mem_perms; + __be32 stag; + __be32 len_hi; + __be32 len_lo; + __be32 va_hi; + __be32 va_lo_fbo; +}; + +#define S_FW_RI_FR_NSMR_WR_QPBINDE 6 +#define M_FW_RI_FR_NSMR_WR_QPBINDE 0x1 +#define V_FW_RI_FR_NSMR_WR_QPBINDE(x) ((x) << S_FW_RI_FR_NSMR_WR_QPBINDE) +#define G_FW_RI_FR_NSMR_WR_QPBINDE(x) \ + (((x) >> S_FW_RI_FR_NSMR_WR_QPBINDE) & M_FW_RI_FR_NSMR_WR_QPBINDE) +#define F_FW_RI_FR_NSMR_WR_QPBINDE V_FW_RI_FR_NSMR_WR_QPBINDE(1U) + +#define S_FW_RI_FR_NSMR_WR_NS 5 +#define M_FW_RI_FR_NSMR_WR_NS 0x1 +#define V_FW_RI_FR_NSMR_WR_NS(x) ((x) << S_FW_RI_FR_NSMR_WR_NS) +#define G_FW_RI_FR_NSMR_WR_NS(x) \ + (((x) >> S_FW_RI_FR_NSMR_WR_NS) & M_FW_RI_FR_NSMR_WR_NS) +#define F_FW_RI_FR_NSMR_WR_NS V_FW_RI_FR_NSMR_WR_NS(1U) + +#define S_FW_RI_FR_NSMR_WR_DCACPU 0 +#define M_FW_RI_FR_NSMR_WR_DCACPU 0x1f +#define V_FW_RI_FR_NSMR_WR_DCACPU(x) ((x) << S_FW_RI_FR_NSMR_WR_DCACPU) +#define G_FW_RI_FR_NSMR_WR_DCACPU(x) \ + (((x) >> S_FW_RI_FR_NSMR_WR_DCACPU) & M_FW_RI_FR_NSMR_WR_DCACPU) + +struct fw_ri_inv_lstag_wr { + __u8 opcode; + __u8 flags; + __u16 wrid; + __u8 r1[3]; + __u8 len16; + __be32 r2; + __be32 stag_inv; +}; + +enum fw_ri_type { + FW_RI_TYPE_INIT, + FW_RI_TYPE_FINI, + FW_RI_TYPE_TERMINATE +}; + +enum fw_ri_init_p2ptype { + FW_RI_INIT_P2PTYPE_RDMA_WRITE = FW_RI_RDMA_WRITE, + FW_RI_INIT_P2PTYPE_READ_REQ = FW_RI_READ_REQ, + FW_RI_INIT_P2PTYPE_SEND = FW_RI_SEND, + FW_RI_INIT_P2PTYPE_SEND_WITH_INV = FW_RI_SEND_WITH_INV, + FW_RI_INIT_P2PTYPE_SEND_WITH_SE = FW_RI_SEND_WITH_SE, + FW_RI_INIT_P2PTYPE_SEND_WITH_SE_INV = FW_RI_SEND_WITH_SE_INV, + FW_RI_INIT_P2PTYPE_DISABLED = 0xf, +}; + +struct fw_ri_wr { + __be32 op_compl; + __be32 flowid_len16; + __u64 cookie; + union fw_ri { + struct fw_ri_init { + __u8 type; + __u8 mpareqbit_p2ptype; + __u8 r4[2]; + __u8 mpa_attrs; + __u8 qp_caps; + __be16 nrqe; + __be32 pdid; + __be32 qpid; + __be32 sq_eqid; + __be32 rq_eqid; + __be32 scqid; + __be32 rcqid; + __be32 ord_max; + __be32 ird_max; + __be32 iss; + __be32 irs; + __be32 hwrqsize; + __be32 hwrqaddr; + __be64 r5; + union fw_ri_init_p2p { + struct fw_ri_rdma_write_wr write; + struct fw_ri_rdma_read_wr read; + struct fw_ri_send_wr send; + } u; + } init; + struct fw_ri_fini { + __u8 type; + __u8 r3[7]; + __be64 r4; + } fini; + struct fw_ri_terminate { + __u8 type; + __u8 r3[3]; + __be32 immdlen; + __u8 termmsg[40]; + } terminate; + } u; +}; + +#define S_FW_RI_WR_MPAREQBIT 7 +#define M_FW_RI_WR_MPAREQBIT 0x1 +#define V_FW_RI_WR_MPAREQBIT(x) ((x) << S_FW_RI_WR_MPAREQBIT) +#define G_FW_RI_WR_MPAREQBIT(x) \ + (((x) >> S_FW_RI_WR_MPAREQBIT) & M_FW_RI_WR_MPAREQBIT) +#define F_FW_RI_WR_MPAREQBIT V_FW_RI_WR_MPAREQBIT(1U) + +#define S_FW_RI_WR_P2PTYPE 0 +#define M_FW_RI_WR_P2PTYPE 0xf +#define V_FW_RI_WR_P2PTYPE(x) ((x) << S_FW_RI_WR_P2PTYPE) +#define G_FW_RI_WR_P2PTYPE(x) \ + (((x) >> S_FW_RI_WR_P2PTYPE) & M_FW_RI_WR_P2PTYPE) + +struct tcp_options { + __be16 mss; + __u8 wsf; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8:4; + __u8 unknown:1; + __u8:1; + __u8 sack:1; + __u8 tstamp:1; +#else + __u8 tstamp:1; + __u8 sack:1; + __u8:1; + __u8 unknown:1; + __u8:4; +#endif +}; + +struct cpl_pass_accept_req { + union opcode_tid ot; + __be16 rsvd; + __be16 len; + __be32 hdr_len; + __be16 vlan; + __be16 l2info; + __be32 tos_stid; + struct tcp_options tcpopt; +}; + +/* cpl_pass_accept_req.hdr_len fields */ +#define S_SYN_RX_CHAN 0 +#define M_SYN_RX_CHAN 0xF +#define V_SYN_RX_CHAN(x) ((x) << S_SYN_RX_CHAN) +#define G_SYN_RX_CHAN(x) (((x) >> S_SYN_RX_CHAN) & M_SYN_RX_CHAN) + +#define S_TCP_HDR_LEN 10 +#define M_TCP_HDR_LEN 0x3F +#define V_TCP_HDR_LEN(x) ((x) << S_TCP_HDR_LEN) +#define G_TCP_HDR_LEN(x) (((x) >> S_TCP_HDR_LEN) & M_TCP_HDR_LEN) + +#define S_IP_HDR_LEN 16 +#define M_IP_HDR_LEN 0x3FF +#define V_IP_HDR_LEN(x) ((x) << S_IP_HDR_LEN) +#define G_IP_HDR_LEN(x) (((x) >> S_IP_HDR_LEN) & M_IP_HDR_LEN) + +#define S_ETH_HDR_LEN 26 +#define M_ETH_HDR_LEN 0x1F +#define V_ETH_HDR_LEN(x) ((x) << S_ETH_HDR_LEN) +#define G_ETH_HDR_LEN(x) (((x) >> S_ETH_HDR_LEN) & M_ETH_HDR_LEN) + +/* cpl_pass_accept_req.l2info fields */ +#define S_SYN_MAC_IDX 0 +#define M_SYN_MAC_IDX 0x1FF +#define V_SYN_MAC_IDX(x) ((x) << S_SYN_MAC_IDX) +#define G_SYN_MAC_IDX(x) (((x) >> S_SYN_MAC_IDX) & M_SYN_MAC_IDX) + +#define S_SYN_XACT_MATCH 9 +#define V_SYN_XACT_MATCH(x) ((x) << S_SYN_XACT_MATCH) +#define F_SYN_XACT_MATCH V_SYN_XACT_MATCH(1U) + +#define S_SYN_INTF 12 +#define M_SYN_INTF 0xF +#define V_SYN_INTF(x) ((x) << S_SYN_INTF) +#define G_SYN_INTF(x) (((x) >> S_SYN_INTF) & M_SYN_INTF) + +struct ulptx_idata { + __be32 cmd_more; + __be32 len; +}; + +#define S_ULPTX_NSGE 0 +#define M_ULPTX_NSGE 0xFFFF +#define V_ULPTX_NSGE(x) ((x) << S_ULPTX_NSGE) +#endif /* _T4FW_RI_API_H_ */ diff --git a/drivers/infiniband/hw/cxgb4/user.h b/drivers/infiniband/hw/cxgb4/user.h new file mode 100644 index 000000000000..ed6414abde02 --- /dev/null +++ b/drivers/infiniband/hw/cxgb4/user.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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 __C4IW_USER_H__ +#define __C4IW_USER_H__ + +#define C4IW_UVERBS_ABI_VERSION 1 + +/* + * Make sure that all structs defined in this file remain laid out so + * that they pack the same way on 32-bit and 64-bit architectures (to + * avoid incompatibility between 32-bit userspace and 64-bit kernels). + * In particular do not use pointer types -- pass pointers in __u64 + * instead. + */ +struct c4iw_create_cq_resp { + __u64 key; + __u64 gts_key; + __u64 memsize; + __u32 cqid; + __u32 size; + __u32 qid_mask; +}; + +struct c4iw_create_qp_resp { + __u64 sq_key; + __u64 rq_key; + __u64 sq_db_gts_key; + __u64 rq_db_gts_key; + __u64 sq_memsize; + __u64 rq_memsize; + __u32 sqid; + __u32 rqid; + __u32 sq_size; + __u32 rq_size; + __u32 qid_mask; +}; +#endif diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c index 37d12e5efa49..1d7aea132a09 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6110.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c @@ -1474,7 +1474,7 @@ static void ipath_ht_quiet_serdes(struct ipath_devdata *dd) /** * ipath_pe_put_tid - write a TID in chip * @dd: the infinipath device - * @tidptr: pointer to the expected TID (in chip) to udpate + * @tidptr: pointer to the expected TID (in chip) to update * @tidtype: RCVHQ_RCV_TYPE_EAGER (1) for eager, RCVHQ_RCV_TYPE_EXPECTED (0) for expected * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing * diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c index fbf8c5379ea8..4b4a30b0dabd 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6120.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c @@ -1328,7 +1328,7 @@ bail: /** * ipath_pe_put_tid - write a TID in chip * @dd: the infinipath device - * @tidptr: pointer to the expected TID (in chip) to udpate + * @tidptr: pointer to the expected TID (in chip) to update * @tidtype: RCVHQ_RCV_TYPE_EAGER (1) for eager, RCVHQ_RCV_TYPE_EXPECTED (0) for expected * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing * @@ -1394,7 +1394,7 @@ static void ipath_pe_put_tid(struct ipath_devdata *dd, u64 __iomem *tidptr, /** * ipath_pe_put_tid_2 - write a TID in chip, Revision 2 or higher * @dd: the infinipath device - * @tidptr: pointer to the expected TID (in chip) to udpate + * @tidptr: pointer to the expected TID (in chip) to update * @tidtype: RCVHQ_RCV_TYPE_EAGER (1) for eager, RCVHQ_RCV_TYPE_EXPECTED (0) for expected * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing * diff --git a/drivers/infiniband/hw/ipath/ipath_iba7220.c b/drivers/infiniband/hw/ipath/ipath_iba7220.c index a805402dd4ae..34b778ed97fc 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba7220.c +++ b/drivers/infiniband/hw/ipath/ipath_iba7220.c @@ -1738,7 +1738,7 @@ bail: /** * ipath_7220_put_tid - write a TID to the chip * @dd: the infinipath device - * @tidptr: pointer to the expected TID (in chip) to udpate + * @tidptr: pointer to the expected TID (in chip) to update * @tidtype: 0 for eager, 1 for expected * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing * diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index cc2ddd29ac57..5a219a2fdf16 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -661,6 +661,14 @@ repoll: wc->opcode = IB_WC_FETCH_ADD; wc->byte_len = 8; break; + case MLX4_OPCODE_MASKED_ATOMIC_CS: + wc->opcode = IB_WC_MASKED_COMP_SWAP; + wc->byte_len = 8; + break; + case MLX4_OPCODE_MASKED_ATOMIC_FA: + wc->opcode = IB_WC_MASKED_FETCH_ADD; + wc->byte_len = 8; + break; case MLX4_OPCODE_BIND_MW: wc->opcode = IB_WC_BIND_MW; break; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 01f2a3f93355..39051417054c 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -139,6 +139,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->local_ca_ack_delay = dev->dev->caps.local_ca_ack_delay; props->atomic_cap = dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_ATOMIC ? IB_ATOMIC_HCA : IB_ATOMIC_NONE; + props->masked_atomic_cap = IB_ATOMIC_HCA; props->max_pkeys = dev->dev->caps.pkey_table_len[1]; props->max_mcast_grp = dev->dev->caps.num_mgms + dev->dev->caps.num_amgms; props->max_mcast_qp_attach = dev->dev->caps.num_qp_per_mgm; diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 5643f4a8ffef..6a60827b2301 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -74,17 +74,19 @@ enum { }; static const __be32 mlx4_ib_opcode[] = { - [IB_WR_SEND] = cpu_to_be32(MLX4_OPCODE_SEND), - [IB_WR_LSO] = cpu_to_be32(MLX4_OPCODE_LSO), - [IB_WR_SEND_WITH_IMM] = cpu_to_be32(MLX4_OPCODE_SEND_IMM), - [IB_WR_RDMA_WRITE] = cpu_to_be32(MLX4_OPCODE_RDMA_WRITE), - [IB_WR_RDMA_WRITE_WITH_IMM] = cpu_to_be32(MLX4_OPCODE_RDMA_WRITE_IMM), - [IB_WR_RDMA_READ] = cpu_to_be32(MLX4_OPCODE_RDMA_READ), - [IB_WR_ATOMIC_CMP_AND_SWP] = cpu_to_be32(MLX4_OPCODE_ATOMIC_CS), - [IB_WR_ATOMIC_FETCH_AND_ADD] = cpu_to_be32(MLX4_OPCODE_ATOMIC_FA), - [IB_WR_SEND_WITH_INV] = cpu_to_be32(MLX4_OPCODE_SEND_INVAL), - [IB_WR_LOCAL_INV] = cpu_to_be32(MLX4_OPCODE_LOCAL_INVAL), - [IB_WR_FAST_REG_MR] = cpu_to_be32(MLX4_OPCODE_FMR), + [IB_WR_SEND] = cpu_to_be32(MLX4_OPCODE_SEND), + [IB_WR_LSO] = cpu_to_be32(MLX4_OPCODE_LSO), + [IB_WR_SEND_WITH_IMM] = cpu_to_be32(MLX4_OPCODE_SEND_IMM), + [IB_WR_RDMA_WRITE] = cpu_to_be32(MLX4_OPCODE_RDMA_WRITE), + [IB_WR_RDMA_WRITE_WITH_IMM] = cpu_to_be32(MLX4_OPCODE_RDMA_WRITE_IMM), + [IB_WR_RDMA_READ] = cpu_to_be32(MLX4_OPCODE_RDMA_READ), + [IB_WR_ATOMIC_CMP_AND_SWP] = cpu_to_be32(MLX4_OPCODE_ATOMIC_CS), + [IB_WR_ATOMIC_FETCH_AND_ADD] = cpu_to_be32(MLX4_OPCODE_ATOMIC_FA), + [IB_WR_SEND_WITH_INV] = cpu_to_be32(MLX4_OPCODE_SEND_INVAL), + [IB_WR_LOCAL_INV] = cpu_to_be32(MLX4_OPCODE_LOCAL_INVAL), + [IB_WR_FAST_REG_MR] = cpu_to_be32(MLX4_OPCODE_FMR), + [IB_WR_MASKED_ATOMIC_CMP_AND_SWP] = cpu_to_be32(MLX4_OPCODE_MASKED_ATOMIC_CS), + [IB_WR_MASKED_ATOMIC_FETCH_AND_ADD] = cpu_to_be32(MLX4_OPCODE_MASKED_ATOMIC_FA), }; static struct mlx4_ib_sqp *to_msqp(struct mlx4_ib_qp *mqp) @@ -1407,6 +1409,9 @@ static void set_atomic_seg(struct mlx4_wqe_atomic_seg *aseg, struct ib_send_wr * if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) { aseg->swap_add = cpu_to_be64(wr->wr.atomic.swap); aseg->compare = cpu_to_be64(wr->wr.atomic.compare_add); + } else if (wr->opcode == IB_WR_MASKED_ATOMIC_FETCH_AND_ADD) { + aseg->swap_add = cpu_to_be64(wr->wr.atomic.compare_add); + aseg->compare = cpu_to_be64(wr->wr.atomic.compare_add_mask); } else { aseg->swap_add = cpu_to_be64(wr->wr.atomic.compare_add); aseg->compare = 0; @@ -1414,6 +1419,15 @@ static void set_atomic_seg(struct mlx4_wqe_atomic_seg *aseg, struct ib_send_wr * } +static void set_masked_atomic_seg(struct mlx4_wqe_masked_atomic_seg *aseg, + struct ib_send_wr *wr) +{ + aseg->swap_add = cpu_to_be64(wr->wr.atomic.swap); + aseg->swap_add_mask = cpu_to_be64(wr->wr.atomic.swap_mask); + aseg->compare = cpu_to_be64(wr->wr.atomic.compare_add); + aseg->compare_mask = cpu_to_be64(wr->wr.atomic.compare_add_mask); +} + static void set_datagram_seg(struct mlx4_wqe_datagram_seg *dseg, struct ib_send_wr *wr) { @@ -1567,6 +1581,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, switch (wr->opcode) { case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: + case IB_WR_MASKED_ATOMIC_FETCH_AND_ADD: set_raddr_seg(wqe, wr->wr.atomic.remote_addr, wr->wr.atomic.rkey); wqe += sizeof (struct mlx4_wqe_raddr_seg); @@ -1579,6 +1594,19 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, break; + case IB_WR_MASKED_ATOMIC_CMP_AND_SWP: + set_raddr_seg(wqe, wr->wr.atomic.remote_addr, + wr->wr.atomic.rkey); + wqe += sizeof (struct mlx4_wqe_raddr_seg); + + set_masked_atomic_seg(wqe, wr); + wqe += sizeof (struct mlx4_wqe_masked_atomic_seg); + + size += (sizeof (struct mlx4_wqe_raddr_seg) + + sizeof (struct mlx4_wqe_masked_atomic_seg)) / 16; + + break; + case IB_WR_RDMA_READ: case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: diff --git a/drivers/infiniband/hw/mthca/mthca_allocator.c b/drivers/infiniband/hw/mthca/mthca_allocator.c index c5ccc2daab60..b4e0cf4e95cd 100644 --- a/drivers/infiniband/hw/mthca/mthca_allocator.c +++ b/drivers/infiniband/hw/mthca/mthca_allocator.c @@ -211,7 +211,7 @@ int mthca_buf_alloc(struct mthca_dev *dev, int size, int max_direct, if (!buf->direct.buf) return -ENOMEM; - pci_unmap_addr_set(&buf->direct, mapping, t); + dma_unmap_addr_set(&buf->direct, mapping, t); memset(buf->direct.buf, 0, size); @@ -251,7 +251,7 @@ int mthca_buf_alloc(struct mthca_dev *dev, int size, int max_direct, goto err_free; dma_list[i] = t; - pci_unmap_addr_set(&buf->page_list[i], mapping, t); + dma_unmap_addr_set(&buf->page_list[i], mapping, t); clear_page(buf->page_list[i].buf); } @@ -289,12 +289,12 @@ void mthca_buf_free(struct mthca_dev *dev, int size, union mthca_buf *buf, if (is_direct) dma_free_coherent(&dev->pdev->dev, size, buf->direct.buf, - pci_unmap_addr(&buf->direct, mapping)); + dma_unmap_addr(&buf->direct, mapping)); else { for (i = 0; i < (size + PAGE_SIZE - 1) / PAGE_SIZE; ++i) dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, buf->page_list[i].buf, - pci_unmap_addr(&buf->page_list[i], + dma_unmap_addr(&buf->page_list[i], mapping)); kfree(buf->page_list); } diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c index 9388164b6053..8e8c728aff88 100644 --- a/drivers/infiniband/hw/mthca/mthca_eq.c +++ b/drivers/infiniband/hw/mthca/mthca_eq.c @@ -504,7 +504,7 @@ static int mthca_create_eq(struct mthca_dev *dev, goto err_out_free_pages; dma_list[i] = t; - pci_unmap_addr_set(&eq->page_list[i], mapping, t); + dma_unmap_addr_set(&eq->page_list[i], mapping, t); clear_page(eq->page_list[i].buf); } @@ -579,7 +579,7 @@ static int mthca_create_eq(struct mthca_dev *dev, if (eq->page_list[i].buf) dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, eq->page_list[i].buf, - pci_unmap_addr(&eq->page_list[i], + dma_unmap_addr(&eq->page_list[i], mapping)); mthca_free_mailbox(dev, mailbox); @@ -629,7 +629,7 @@ static void mthca_free_eq(struct mthca_dev *dev, for (i = 0; i < npages; ++i) pci_free_consistent(dev->pdev, PAGE_SIZE, eq->page_list[i].buf, - pci_unmap_addr(&eq->page_list[i], mapping)); + dma_unmap_addr(&eq->page_list[i], mapping)); kfree(eq->page_list); mthca_free_mailbox(dev, mailbox); diff --git a/drivers/infiniband/hw/mthca/mthca_provider.h b/drivers/infiniband/hw/mthca/mthca_provider.h index 90f4c4d2e983..596acc45569b 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.h +++ b/drivers/infiniband/hw/mthca/mthca_provider.h @@ -46,7 +46,7 @@ struct mthca_buf_list { void *buf; - DECLARE_PCI_UNMAP_ADDR(mapping) + DEFINE_DMA_UNMAP_ADDR(mapping); }; union mthca_buf { diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c index c36a3f514929..86acb7d57064 100644 --- a/drivers/infiniband/hw/nes/nes_hw.c +++ b/drivers/infiniband/hw/nes/nes_hw.c @@ -1297,7 +1297,7 @@ int nes_destroy_cqp(struct nes_device *nesdev) /** * nes_init_1g_phy */ -int nes_init_1g_phy(struct nes_device *nesdev, u8 phy_type, u8 phy_index) +static int nes_init_1g_phy(struct nes_device *nesdev, u8 phy_type, u8 phy_index) { u32 counter = 0; u16 phy_data; @@ -1351,7 +1351,7 @@ int nes_init_1g_phy(struct nes_device *nesdev, u8 phy_type, u8 phy_index) /** * nes_init_2025_phy */ -int nes_init_2025_phy(struct nes_device *nesdev, u8 phy_type, u8 phy_index) +static int nes_init_2025_phy(struct nes_device *nesdev, u8 phy_type, u8 phy_index) { u32 temp_phy_data = 0; u32 temp_phy_data2 = 0; @@ -2458,7 +2458,6 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number) return; } nesadapter->mac_sw_state[mac_number] = NES_MAC_SW_INTERRUPT; - spin_unlock_irqrestore(&nesadapter->phy_lock, flags); /* ack the MAC interrupt */ mac_status = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS + (mac_index * 0x200)); @@ -2469,11 +2468,9 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number) if (mac_status & (NES_MAC_INT_LINK_STAT_CHG | NES_MAC_INT_XGMII_EXT)) { nesdev->link_status_interrupts++; - if (0 == (++nesadapter->link_interrupt_count[mac_index] % ((u16)NES_MAX_LINK_INTERRUPTS))) { - spin_lock_irqsave(&nesadapter->phy_lock, flags); + if (0 == (++nesadapter->link_interrupt_count[mac_index] % ((u16)NES_MAX_LINK_INTERRUPTS))) nes_reset_link(nesdev, mac_index); - spin_unlock_irqrestore(&nesadapter->phy_lock, flags); - } + /* read the PHY interrupt status register */ if ((nesadapter->OneG_Mode) && (nesadapter->phy_type[mac_index] != NES_PHY_TYPE_PUMA_1G)) { @@ -2587,6 +2584,7 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number) break; } } + spin_unlock_irqrestore(&nesadapter->phy_lock, flags); if (phy_data & 0x0004) { if (wide_ppm_offset && diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index b7c813f4be43..9f4cadf9f851 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -1461,11 +1461,14 @@ static int nes_netdev_get_settings(struct net_device *netdev, struct ethtool_cmd et_cmd->transceiver = XCVR_INTERNAL; et_cmd->phy_address = mac_index; } else { + unsigned long flags; et_cmd->supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg; et_cmd->advertising = ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg; + spin_lock_irqsave(&nesadapter->phy_lock, flags); nes_read_1G_phy_reg(nesdev, 0, phy_index, &phy_data); + spin_unlock_irqrestore(&nesadapter->phy_lock, flags); if (phy_data & 0x1000) et_cmd->autoneg = AUTONEG_ENABLE; else @@ -1503,12 +1506,15 @@ static int nes_netdev_set_settings(struct net_device *netdev, struct ethtool_cmd struct nes_vnic *nesvnic = netdev_priv(netdev); struct nes_device *nesdev = nesvnic->nesdev; struct nes_adapter *nesadapter = nesdev->nesadapter; - u16 phy_data; if ((nesadapter->OneG_Mode) && (nesadapter->phy_type[nesdev->mac_index] != NES_PHY_TYPE_PUMA_1G)) { - nes_read_1G_phy_reg(nesdev, 0, nesadapter->phy_index[nesdev->mac_index], - &phy_data); + unsigned long flags; + u16 phy_data; + u8 phy_index = nesadapter->phy_index[nesdev->mac_index]; + + spin_lock_irqsave(&nesadapter->phy_lock, flags); + nes_read_1G_phy_reg(nesdev, 0, phy_index, &phy_data); if (et_cmd->autoneg) { /* Turn on Full duplex, Autoneg, and restart autonegotiation */ phy_data |= 0x1300; @@ -1516,8 +1522,8 @@ static int nes_netdev_set_settings(struct net_device *netdev, struct ethtool_cmd /* Turn off autoneg */ phy_data &= ~0x1000; } - nes_write_1G_phy_reg(nesdev, 0, nesadapter->phy_index[nesdev->mac_index], - phy_data); + nes_write_1G_phy_reg(nesdev, 0, phy_index, phy_data); + spin_unlock_irqrestore(&nesadapter->phy_lock, flags); } return 0; diff --git a/drivers/infiniband/hw/nes/nes_utils.c b/drivers/infiniband/hw/nes/nes_utils.c index 186623d86959..a9f5dd272f1a 100644 --- a/drivers/infiniband/hw/nes/nes_utils.c +++ b/drivers/infiniband/hw/nes/nes_utils.c @@ -381,12 +381,8 @@ static u16 nes_read16_eeprom(void __iomem *addr, u16 offset) */ void nes_write_1G_phy_reg(struct nes_device *nesdev, u8 phy_reg, u8 phy_addr, u16 data) { - struct nes_adapter *nesadapter = nesdev->nesadapter; u32 u32temp; u32 counter; - unsigned long flags; - - spin_lock_irqsave(&nesadapter->phy_lock, flags); nes_write_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL, 0x50020000 | data | ((u32)phy_reg << 18) | ((u32)phy_addr << 23)); @@ -402,8 +398,6 @@ void nes_write_1G_phy_reg(struct nes_device *nesdev, u8 phy_reg, u8 phy_addr, u1 if (!(u32temp & 1)) nes_debug(NES_DBG_PHY, "Phy is not responding. interrupt status = 0x%X.\n", u32temp); - - spin_unlock_irqrestore(&nesadapter->phy_lock, flags); } @@ -414,14 +408,11 @@ void nes_write_1G_phy_reg(struct nes_device *nesdev, u8 phy_reg, u8 phy_addr, u1 */ void nes_read_1G_phy_reg(struct nes_device *nesdev, u8 phy_reg, u8 phy_addr, u16 *data) { - struct nes_adapter *nesadapter = nesdev->nesadapter; u32 u32temp; u32 counter; - unsigned long flags; /* nes_debug(NES_DBG_PHY, "phy addr = %d, mac_index = %d\n", phy_addr, nesdev->mac_index); */ - spin_lock_irqsave(&nesadapter->phy_lock, flags); nes_write_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL, 0x60020000 | ((u32)phy_reg << 18) | ((u32)phy_addr << 23)); @@ -441,7 +432,6 @@ void nes_read_1G_phy_reg(struct nes_device *nesdev, u8 phy_reg, u8 phy_addr, u16 } else { *data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL); } - spin_unlock_irqrestore(&nesadapter->phy_lock, flags); } diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index e54f312e4bdc..925e1f2d1d55 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -374,7 +374,7 @@ static int alloc_fast_reg_mr(struct nes_device *nesdev, struct nes_pd *nespd, /* * nes_alloc_fast_reg_mr */ -struct ib_mr *nes_alloc_fast_reg_mr(struct ib_pd *ibpd, int max_page_list_len) +static struct ib_mr *nes_alloc_fast_reg_mr(struct ib_pd *ibpd, int max_page_list_len) { struct nes_pd *nespd = to_nespd(ibpd); struct nes_vnic *nesvnic = to_nesvnic(ibpd->device); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c index d10b4ec68d28..40e858492f90 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c @@ -49,6 +49,25 @@ static u32 ipoib_get_rx_csum(struct net_device *dev) !test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags); } +static int ipoib_set_tso(struct net_device *dev, u32 data) +{ + struct ipoib_dev_priv *priv = netdev_priv(dev); + + if (data) { + if (!test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags) && + (dev->features & NETIF_F_SG) && + (priv->hca_caps & IB_DEVICE_UD_TSO)) { + dev->features |= NETIF_F_TSO; + } else { + ipoib_warn(priv, "can't set TSO on\n"); + return -EOPNOTSUPP; + } + } else + dev->features &= ~NETIF_F_TSO; + + return 0; +} + static int ipoib_get_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) { @@ -131,6 +150,7 @@ static void ipoib_get_ethtool_stats(struct net_device *dev, static const struct ethtool_ops ipoib_ethtool_ops = { .get_drvinfo = ipoib_get_drvinfo, .get_rx_csum = ipoib_get_rx_csum, + .set_tso = ipoib_set_tso, .get_coalesce = ipoib_get_coalesce, .set_coalesce = ipoib_set_coalesce, .get_flags = ethtool_op_get_flags, diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 93399dff0c6f..7b2fc98e2f2b 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -325,7 +325,7 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn) */ if (ib_conn) { ib_conn->iser_conn = NULL; - iser_conn_put(ib_conn); + iser_conn_put(ib_conn, 1); /* deref iscsi/ib conn unbinding */ } } @@ -357,11 +357,12 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session, /* binds the iSER connection retrieved from the previously * connected ep_handle to the iSCSI layer connection. exchanges * connection pointers */ - iser_err("binding iscsi conn %p to iser_conn %p\n",conn,ib_conn); + iser_err("binding iscsi/iser conn %p %p to ib_conn %p\n", + conn, conn->dd_data, ib_conn); iser_conn = conn->dd_data; ib_conn->iser_conn = iser_conn; iser_conn->ib_conn = ib_conn; - iser_conn_get(ib_conn); + iser_conn_get(ib_conn); /* ref iscsi/ib conn binding */ return 0; } @@ -382,7 +383,7 @@ iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) * There is no unbind event so the stop callback * must release the ref from the bind. */ - iser_conn_put(ib_conn); + iser_conn_put(ib_conn, 1); /* deref iscsi/ib conn unbinding */ } iser_conn->ib_conn = NULL; } diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index 036934cdcb92..f1df01567bb6 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -232,6 +232,7 @@ struct iser_device { struct ib_cq *tx_cq; struct ib_mr *mr; struct tasklet_struct cq_tasklet; + struct ib_event_handler event_handler; struct list_head ig_list; /* entry in ig devices list */ int refcount; }; @@ -246,7 +247,6 @@ struct iser_conn { struct rdma_cm_id *cma_id; /* CMA ID */ struct ib_qp *qp; /* QP */ struct ib_fmr_pool *fmr_pool; /* pool of IB FMRs */ - int disc_evt_flag; /* disconn event delivered */ wait_queue_head_t wait; /* waitq for conn/disconn */ int post_recv_buf_count; /* posted rx count */ atomic_t post_send_buf_count; /* posted tx count */ @@ -320,7 +320,7 @@ void iser_conn_init(struct iser_conn *ib_conn); void iser_conn_get(struct iser_conn *ib_conn); -void iser_conn_put(struct iser_conn *ib_conn); +int iser_conn_put(struct iser_conn *ib_conn, int destroy_cma_id_allowed); void iser_conn_terminate(struct iser_conn *ib_conn); diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index b89d76b39a13..9876865732f7 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -54,6 +54,13 @@ static void iser_qp_event_callback(struct ib_event *cause, void *context) iser_err("got qp event %d\n",cause->event); } +static void iser_event_handler(struct ib_event_handler *handler, + struct ib_event *event) +{ + iser_err("async event %d on device %s port %d\n", event->event, + event->device->name, event->element.port_num); +} + /** * iser_create_device_ib_res - creates Protection Domain (PD), Completion * Queue (CQ), DMA Memory Region (DMA MR) with the device associated with @@ -96,8 +103,15 @@ static int iser_create_device_ib_res(struct iser_device *device) if (IS_ERR(device->mr)) goto dma_mr_err; + INIT_IB_EVENT_HANDLER(&device->event_handler, device->ib_device, + iser_event_handler); + if (ib_register_event_handler(&device->event_handler)) + goto handler_err; + return 0; +handler_err: + ib_dereg_mr(device->mr); dma_mr_err: tasklet_kill(&device->cq_tasklet); cq_arm_err: @@ -120,7 +134,7 @@ static void iser_free_device_ib_res(struct iser_device *device) BUG_ON(device->mr == NULL); tasklet_kill(&device->cq_tasklet); - + (void)ib_unregister_event_handler(&device->event_handler); (void)ib_dereg_mr(device->mr); (void)ib_destroy_cq(device->tx_cq); (void)ib_destroy_cq(device->rx_cq); @@ -149,10 +163,8 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn) device = ib_conn->device; ib_conn->login_buf = kmalloc(ISER_RX_LOGIN_SIZE, GFP_KERNEL); - if (!ib_conn->login_buf) { - goto alloc_err; - ret = -ENOMEM; - } + if (!ib_conn->login_buf) + goto out_err; ib_conn->login_dma = ib_dma_map_single(ib_conn->device->ib_device, (void *)ib_conn->login_buf, ISER_RX_LOGIN_SIZE, @@ -161,10 +173,9 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn) ib_conn->page_vec = kmalloc(sizeof(struct iser_page_vec) + (sizeof(u64) * (ISCSI_ISER_SG_TABLESIZE +1)), GFP_KERNEL); - if (!ib_conn->page_vec) { - ret = -ENOMEM; - goto alloc_err; - } + if (!ib_conn->page_vec) + goto out_err; + ib_conn->page_vec->pages = (u64 *) (ib_conn->page_vec + 1); params.page_shift = SHIFT_4K; @@ -184,7 +195,8 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn) ib_conn->fmr_pool = ib_create_fmr_pool(device->pd, ¶ms); if (IS_ERR(ib_conn->fmr_pool)) { ret = PTR_ERR(ib_conn->fmr_pool); - goto fmr_pool_err; + ib_conn->fmr_pool = NULL; + goto out_err; } memset(&init_attr, 0, sizeof init_attr); @@ -202,7 +214,7 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn) ret = rdma_create_qp(ib_conn->cma_id, device->pd, &init_attr); if (ret) - goto qp_err; + goto out_err; ib_conn->qp = ib_conn->cma_id->qp; iser_err("setting conn %p cma_id %p: fmr_pool %p qp %p\n", @@ -210,12 +222,7 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn) ib_conn->fmr_pool, ib_conn->cma_id->qp); return ret; -qp_err: - (void)ib_destroy_fmr_pool(ib_conn->fmr_pool); -fmr_pool_err: - kfree(ib_conn->page_vec); - kfree(ib_conn->login_buf); -alloc_err: +out_err: iser_err("unable to alloc mem or create resource, err %d\n", ret); return ret; } @@ -224,7 +231,7 @@ alloc_err: * releases the FMR pool, QP and CMA ID objects, returns 0 on success, * -1 on failure */ -static int iser_free_ib_conn_res(struct iser_conn *ib_conn) +static int iser_free_ib_conn_res(struct iser_conn *ib_conn, int can_destroy_id) { BUG_ON(ib_conn == NULL); @@ -239,7 +246,8 @@ static int iser_free_ib_conn_res(struct iser_conn *ib_conn) if (ib_conn->qp != NULL) rdma_destroy_qp(ib_conn->cma_id); - if (ib_conn->cma_id != NULL) + /* if cma handler context, the caller acts s.t the cma destroy the id */ + if (ib_conn->cma_id != NULL && can_destroy_id) rdma_destroy_id(ib_conn->cma_id); ib_conn->fmr_pool = NULL; @@ -317,7 +325,7 @@ static int iser_conn_state_comp_exch(struct iser_conn *ib_conn, /** * Frees all conn objects and deallocs conn descriptor */ -static void iser_conn_release(struct iser_conn *ib_conn) +static void iser_conn_release(struct iser_conn *ib_conn, int can_destroy_id) { struct iser_device *device = ib_conn->device; @@ -327,13 +335,11 @@ static void iser_conn_release(struct iser_conn *ib_conn) list_del(&ib_conn->conn_list); mutex_unlock(&ig.connlist_mutex); iser_free_rx_descriptors(ib_conn); - iser_free_ib_conn_res(ib_conn); + iser_free_ib_conn_res(ib_conn, can_destroy_id); ib_conn->device = NULL; /* on EVENT_ADDR_ERROR there's no device yet for this conn */ if (device != NULL) iser_device_try_release(device); - if (ib_conn->iser_conn) - ib_conn->iser_conn->ib_conn = NULL; iscsi_destroy_endpoint(ib_conn->ep); } @@ -342,10 +348,13 @@ void iser_conn_get(struct iser_conn *ib_conn) atomic_inc(&ib_conn->refcount); } -void iser_conn_put(struct iser_conn *ib_conn) +int iser_conn_put(struct iser_conn *ib_conn, int can_destroy_id) { - if (atomic_dec_and_test(&ib_conn->refcount)) - iser_conn_release(ib_conn); + if (atomic_dec_and_test(&ib_conn->refcount)) { + iser_conn_release(ib_conn, can_destroy_id); + return 1; + } + return 0; } /** @@ -369,19 +378,20 @@ void iser_conn_terminate(struct iser_conn *ib_conn) wait_event_interruptible(ib_conn->wait, ib_conn->state == ISER_CONN_DOWN); - iser_conn_put(ib_conn); + iser_conn_put(ib_conn, 1); /* deref ib conn deallocate */ } -static void iser_connect_error(struct rdma_cm_id *cma_id) +static int iser_connect_error(struct rdma_cm_id *cma_id) { struct iser_conn *ib_conn; ib_conn = (struct iser_conn *)cma_id->context; ib_conn->state = ISER_CONN_DOWN; wake_up_interruptible(&ib_conn->wait); + return iser_conn_put(ib_conn, 0); /* deref ib conn's cma id */ } -static void iser_addr_handler(struct rdma_cm_id *cma_id) +static int iser_addr_handler(struct rdma_cm_id *cma_id) { struct iser_device *device; struct iser_conn *ib_conn; @@ -390,8 +400,7 @@ static void iser_addr_handler(struct rdma_cm_id *cma_id) device = iser_device_find_by_ib_device(cma_id); if (!device) { iser_err("device lookup/creation failed\n"); - iser_connect_error(cma_id); - return; + return iser_connect_error(cma_id); } ib_conn = (struct iser_conn *)cma_id->context; @@ -400,11 +409,13 @@ static void iser_addr_handler(struct rdma_cm_id *cma_id) ret = rdma_resolve_route(cma_id, 1000); if (ret) { iser_err("resolve route failed: %d\n", ret); - iser_connect_error(cma_id); + return iser_connect_error(cma_id); } + + return 0; } -static void iser_route_handler(struct rdma_cm_id *cma_id) +static int iser_route_handler(struct rdma_cm_id *cma_id) { struct rdma_conn_param conn_param; int ret; @@ -425,9 +436,9 @@ static void iser_route_handler(struct rdma_cm_id *cma_id) goto failure; } - return; + return 0; failure: - iser_connect_error(cma_id); + return iser_connect_error(cma_id); } static void iser_connected_handler(struct rdma_cm_id *cma_id) @@ -439,12 +450,12 @@ static void iser_connected_handler(struct rdma_cm_id *cma_id) wake_up_interruptible(&ib_conn->wait); } -static void iser_disconnected_handler(struct rdma_cm_id *cma_id) +static int iser_disconnected_handler(struct rdma_cm_id *cma_id) { struct iser_conn *ib_conn; + int ret; ib_conn = (struct iser_conn *)cma_id->context; - ib_conn->disc_evt_flag = 1; /* getting here when the state is UP means that the conn is being * * terminated asynchronously from the iSCSI layer's perspective. */ @@ -459,20 +470,24 @@ static void iser_disconnected_handler(struct rdma_cm_id *cma_id) ib_conn->state = ISER_CONN_DOWN; wake_up_interruptible(&ib_conn->wait); } + + ret = iser_conn_put(ib_conn, 0); /* deref ib conn's cma id */ + return ret; } static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) { int ret = 0; - iser_err("event %d conn %p id %p\n",event->event,cma_id->context,cma_id); + iser_err("event %d status %d conn %p id %p\n", + event->event, event->status, cma_id->context, cma_id); switch (event->event) { case RDMA_CM_EVENT_ADDR_RESOLVED: - iser_addr_handler(cma_id); + ret = iser_addr_handler(cma_id); break; case RDMA_CM_EVENT_ROUTE_RESOLVED: - iser_route_handler(cma_id); + ret = iser_route_handler(cma_id); break; case RDMA_CM_EVENT_ESTABLISHED: iser_connected_handler(cma_id); @@ -482,13 +497,12 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve case RDMA_CM_EVENT_CONNECT_ERROR: case RDMA_CM_EVENT_UNREACHABLE: case RDMA_CM_EVENT_REJECTED: - iser_err("event: %d, error: %d\n", event->event, event->status); - iser_connect_error(cma_id); + ret = iser_connect_error(cma_id); break; case RDMA_CM_EVENT_DISCONNECTED: case RDMA_CM_EVENT_DEVICE_REMOVAL: case RDMA_CM_EVENT_ADDR_CHANGE: - iser_disconnected_handler(cma_id); + ret = iser_disconnected_handler(cma_id); break; default: iser_err("Unexpected RDMA CM event (%d)\n", event->event); @@ -503,7 +517,7 @@ void iser_conn_init(struct iser_conn *ib_conn) init_waitqueue_head(&ib_conn->wait); ib_conn->post_recv_buf_count = 0; atomic_set(&ib_conn->post_send_buf_count, 0); - atomic_set(&ib_conn->refcount, 1); + atomic_set(&ib_conn->refcount, 1); /* ref ib conn allocation */ INIT_LIST_HEAD(&ib_conn->conn_list); spin_lock_init(&ib_conn->lock); } @@ -531,6 +545,7 @@ int iser_connect(struct iser_conn *ib_conn, ib_conn->state = ISER_CONN_PENDING; + iser_conn_get(ib_conn); /* ref ib conn's cma id */ ib_conn->cma_id = rdma_create_id(iser_cma_handler, (void *)ib_conn, RDMA_PS_TCP); @@ -568,7 +583,7 @@ id_failure: addr_failure: ib_conn->state = ISER_CONN_DOWN; connect_failure: - iser_conn_release(ib_conn); + iser_conn_release(ib_conn, 1); return err; } @@ -737,12 +752,10 @@ static void iser_handle_comp_error(struct iser_tx_desc *desc, iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn, ISCSI_ERR_CONN_FAILED); - /* complete the termination process if disconnect event was delivered * - * note there are no more non completed posts to the QP */ - if (ib_conn->disc_evt_flag) { - ib_conn->state = ISER_CONN_DOWN; - wake_up_interruptible(&ib_conn->wait); - } + /* no more non completed posts to the QP, complete the + * termination process w.o worrying on disconnect event */ + ib_conn->state = ISER_CONN_DOWN; + wake_up_interruptible(&ib_conn->wait); } } diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 7e18bcf05a66..46239e47a260 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -59,11 +59,11 @@ static unsigned int get_time_pit(void) unsigned long flags; unsigned int count; - spin_lock_irqsave(&i8253_lock, flags); + raw_spin_lock_irqsave(&i8253_lock, flags); outb_p(0x00, 0x43); count = inb_p(0x40); count |= inb_p(0x40) << 8; - spin_unlock_irqrestore(&i8253_lock, flags); + raw_spin_unlock_irqrestore(&i8253_lock, flags); return count; } diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index 1c0b529c06aa..4afe0a3b4884 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -146,11 +146,11 @@ static unsigned int get_time_pit(void) unsigned long flags; unsigned int count; - spin_lock_irqsave(&i8253_lock, flags); + raw_spin_lock_irqsave(&i8253_lock, flags); outb_p(0x00, 0x43); count = inb_p(0x40); count |= inb_p(0x40) << 8; - spin_unlock_irqrestore(&i8253_lock, flags); + raw_spin_unlock_irqrestore(&i8253_lock, flags); return count; } diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index ea4e1fd12651..f080dd31499b 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -30,7 +30,7 @@ MODULE_ALIAS("platform:pcspkr"); #include <asm/i8253.h> #else #include <asm/8253pit.h> -static DEFINE_SPINLOCK(i8253_lock); +static DEFINE_RAW_SPINLOCK(i8253_lock); #endif static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) @@ -50,7 +50,7 @@ static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int c if (value > 20 && value < 32767) count = PIT_TICK_RATE / value; - spin_lock_irqsave(&i8253_lock, flags); + raw_spin_lock_irqsave(&i8253_lock, flags); if (count) { /* set command for counter 2, 2 byte write */ @@ -65,7 +65,7 @@ static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int c outb(inb_p(0x61) & 0xFC, 0x61); } - spin_unlock_irqrestore(&i8253_lock, flags); + raw_spin_unlock_irqrestore(&i8253_lock, flags); return 0; } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index cc471983ac0e..b9f58ca82fd1 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -154,8 +154,8 @@ config TOUCHSCREEN_FUJITSU module will be called fujitsu-ts. config TOUCHSCREEN_S3C2410 - tristate "Samsung S3C2410 touchscreen input driver" - depends on ARCH_S3C2410 + tristate "Samsung S3C2410/generic touchscreen input driver" + depends on ARCH_S3C2410 || SAMSUNG_DEV_TS select S3C24XX_ADC help Say Y here if you have the s3c2410 touchscreen. diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 98a7d1279486..e0b7c834111d 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -37,9 +37,7 @@ #include <plat/adc.h> #include <plat/regs-adc.h> - -#include <mach/regs-gpio.h> -#include <mach/ts.h> +#include <plat/ts.h> #define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0)) @@ -57,6 +55,8 @@ S3C2410_ADCTSC_AUTO_PST | \ S3C2410_ADCTSC_XY_PST(0)) +#define FEAT_PEN_IRQ (1 << 0) /* HAS ADCCLRINTPNDNUP */ + /* Per-touchscreen data. */ /** @@ -71,6 +71,7 @@ * @irq_tc: The interrupt number for pen up/down interrupt * @count: The number of samples collected. * @shift: The log2 of the maximum count to read in one go. + * @features: The features supported by the TSADC MOdule. */ struct s3c2410ts { struct s3c_adc_client *client; @@ -83,26 +84,12 @@ struct s3c2410ts { int irq_tc; int count; int shift; + int features; }; static struct s3c2410ts ts; /** - * s3c2410_ts_connect - configure gpio for s3c2410 systems - * - * Configure the GPIO for the S3C2410 system, where we have external FETs - * connected to the device (later systems such as the S3C2440 integrate - * these into the device). -*/ -static inline void s3c2410_ts_connect(void) -{ - s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON); - s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON); - s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON); - s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON); -} - -/** * get_down - return the down state of the pen * @data0: The data read from ADCDAT0 register. * @data1: The data read from ADCDAT1 register. @@ -188,6 +175,11 @@ static irqreturn_t stylus_irq(int irq, void *dev_id) else dev_info(ts.dev, "%s: count=%d\n", __func__, ts.count); + if (ts.features & FEAT_PEN_IRQ) { + /* Clear pen down/up interrupt */ + writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP); + } + return IRQ_HANDLED; } @@ -296,9 +288,9 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev) goto err_clk; } - /* Configure the touchscreen external FETs on the S3C2410 */ - if (!platform_get_device_id(pdev)->driver_data) - s3c2410_ts_connect(); + /* inititalise the gpio */ + if (info->cfg_gpio) + info->cfg_gpio(to_platform_device(ts.dev)); ts.client = s3c_adc_register(pdev, s3c24xx_ts_select, s3c24xx_ts_conversion, 1); @@ -334,6 +326,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev) ts.input->id.version = 0x0102; ts.shift = info->oversampling_shift; + ts.features = platform_get_device_id(pdev)->driver_data; ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED, "s3c2410_ts_pen", ts.input); @@ -420,15 +413,14 @@ static struct dev_pm_ops s3c_ts_pmops = { #endif static struct platform_device_id s3cts_driver_ids[] = { - { "s3c2410-ts", 0 }, - { "s3c2440-ts", 1 }, + { "s3c64xx-ts", FEAT_PEN_IRQ }, { } }; MODULE_DEVICE_TABLE(platform, s3cts_driver_ids); static struct platform_driver s3c_ts_driver = { .driver = { - .name = "s3c24xx-ts", + .name = "samsung-ts", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &s3c_ts_pmops, diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c index 9f49d9065791..c53e2417e7d4 100644 --- a/drivers/isdn/divert/divert_procfs.c +++ b/drivers/isdn/divert/divert_procfs.c @@ -20,6 +20,7 @@ #include <linux/sched.h> #include <linux/isdnif.h> #include <net/net_namespace.h> +#include <linux/smp_lock.h> #include "isdn_divert.h" @@ -177,9 +178,7 @@ isdn_divert_close(struct inode *ino, struct file *filep) /*********/ /* IOCTL */ /*********/ -static int -isdn_divert_ioctl(struct inode *inode, struct file *file, - uint cmd, ulong arg) +static int isdn_divert_ioctl_unlocked(struct file *file, uint cmd, ulong arg) { divert_ioctl dioctl; int i; @@ -258,6 +257,17 @@ isdn_divert_ioctl(struct inode *inode, struct file *file, return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0; } /* isdn_divert_ioctl */ +static long isdn_divert_ioctl(struct file *file, uint cmd, ulong arg) +{ + long ret; + + lock_kernel(); + ret = isdn_divert_ioctl_unlocked(file, cmd, arg); + unlock_kernel(); + + return ret; +} + static const struct file_operations isdn_fops = { .owner = THIS_MODULE, @@ -265,7 +275,7 @@ static const struct file_operations isdn_fops = .read = isdn_divert_read, .write = isdn_divert_write, .poll = isdn_divert_poll, - .ioctl = isdn_divert_ioctl, + .unlocked_ioctl = isdn_divert_ioctl, .open = isdn_divert_open, .release = isdn_divert_close, }; diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c index 94b796d84053..f410d0eb2fef 100644 --- a/drivers/isdn/hardware/avm/avm_cs.c +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -13,7 +13,6 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/ptrace.h> -#include <linux/slab.h> #include <linux/string.h> #include <linux/tty.h> #include <linux/serial.h> @@ -61,31 +60,6 @@ static void avmcs_release(struct pcmcia_device *link); static void avmcs_detach(struct pcmcia_device *p_dev); -/* - A linked list of "instances" of the skeleton device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one struct pcmcia_device structure (defined in ds.h). - - You may not want to use a linked list for this -- for example, the - memory card driver uses an array of struct pcmcia_device pointers, where minor - device numbers are used to derive the corresponding array index. -*/ - -/* - A driver needs to provide a dev_node_t structure for each device - on a card. In some cases, there is only one device per card (for - example, ethernet cards, modems). In other cases, there may be - many actual or logical devices (SCSI adapters, memory cards with - multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a struct pcmcia_device - structure. We allocate them in the card's private data structure, - because they generally can't be allocated dynamically. -*/ - -typedef struct local_info_t { - dev_node_t node; -} local_info_t; - /*====================================================================== avmcs_attach() creates an "instance" of the driver, allocating @@ -100,32 +74,19 @@ typedef struct local_info_t { static int avmcs_probe(struct pcmcia_device *p_dev) { - local_info_t *local; /* The io structure describes IO port mapping */ p_dev->io.NumPorts1 = 16; p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; p_dev->io.NumPorts2 = 0; - /* Interrupt setup */ - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - /* General socket configuration */ p_dev->conf.Attributes = CONF_ENABLE_IRQ; p_dev->conf.IntType = INT_MEMORY_AND_IO; p_dev->conf.ConfigIndex = 1; p_dev->conf.Present = PRESENT_OPTION; - /* Allocate space for private device-specific data */ - local = kzalloc(sizeof(local_info_t), GFP_KERNEL); - if (!local) - goto err; - p_dev->priv = local; - return avmcs_config(p_dev); - - err: - return -ENOMEM; } /* avmcs_attach */ /*====================================================================== @@ -140,7 +101,6 @@ static int avmcs_probe(struct pcmcia_device *p_dev) static void avmcs_detach(struct pcmcia_device *link) { avmcs_release(link); - kfree(link->priv); } /* avmcs_detach */ /*====================================================================== @@ -171,14 +131,11 @@ static int avmcs_configcheck(struct pcmcia_device *p_dev, static int avmcs_config(struct pcmcia_device *link) { - local_info_t *dev; - int i; + int i = -1; char devname[128]; int cardtype; int (*addcard)(unsigned int port, unsigned irq); - dev = link->priv; - devname[0] = 0; if (link->prod_id[1]) strlcpy(devname, link->prod_id[1], sizeof(devname)); @@ -190,11 +147,7 @@ static int avmcs_config(struct pcmcia_device *link) return -ENODEV; do { - /* - * allocate an interrupt line - */ - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { + if (!link->irq) { /* undo */ pcmcia_disable_device(link); break; @@ -211,15 +164,11 @@ static int avmcs_config(struct pcmcia_device *link) } while (0); - /* At this point, the dev_node_t structure(s) should be - initialized and arranged in a linked list at link->dev. */ - if (devname[0]) { char *s = strrchr(devname, ' '); if (!s) s = devname; else s++; - strcpy(dev->node.dev_name, s); if (strcmp("M1", s) == 0) { cardtype = AVM_CARDTYPE_M1; } else if (strcmp("M2", s) == 0) { @@ -227,14 +176,8 @@ static int avmcs_config(struct pcmcia_device *link) } else { cardtype = AVM_CARDTYPE_B1; } - } else { - strcpy(dev->node.dev_name, "b1"); + } else cardtype = AVM_CARDTYPE_B1; - } - - dev->node.major = 64; - dev->node.minor = 0; - link->dev_node = &dev->node; /* If any step failed, release any partially configured state */ if (i != 0) { @@ -249,13 +192,12 @@ static int avmcs_config(struct pcmcia_device *link) default: case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break; } - if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) { - printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n", - dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ); - avmcs_release(link); - return -ENODEV; + if ((i = (*addcard)(link->io.BasePort1, link->irq)) < 0) { + dev_err(&link->dev, "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n", + link->io.BasePort1, link->irq); + avmcs_release(link); + return -ENODEV; } - dev->node.minor = i; return 0; } /* avmcs_config */ @@ -270,7 +212,7 @@ static int avmcs_config(struct pcmcia_device *link) static void avmcs_release(struct pcmcia_device *link) { - b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ); + b1pcmcia_delcard(link->io.BasePort1, link->irq); pcmcia_disable_device(link); } /* avmcs_release */ diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index 75e71b5d9215..095ed76ebe80 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -117,7 +117,7 @@ * NOTE: only one mode value must be given for every card. * -> See hfc_multi.h for HFC_IO_MODE_* values * By default, the IO mode is pci memory IO (MEMIO). - * Some cards requre specific IO mode, so it cannot be changed. + * Some cards require specific IO mode, so it cannot be changed. * It may be usefull to set IO mode to register io (REGIO) to solve * PCI bridge problems. * If unsure, don't give this parameter. diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c index 8d1d63a02b34..a80a7617f16f 100644 --- a/drivers/isdn/hisax/avma1_cs.c +++ b/drivers/isdn/hisax/avma1_cs.c @@ -62,31 +62,6 @@ static void avma1cs_release(struct pcmcia_device *link); static void avma1cs_detach(struct pcmcia_device *p_dev) __devexit ; -/* - A linked list of "instances" of the skeleton device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one struct pcmcia_device structure (defined in ds.h). - - You may not want to use a linked list for this -- for example, the - memory card driver uses an array of struct pcmcia_device pointers, where minor - device numbers are used to derive the corresponding array index. -*/ - -/* - A driver needs to provide a dev_node_t structure for each device - on a card. In some cases, there is only one device per card (for - example, ethernet cards, modems). In other cases, there may be - many actual or logical devices (SCSI adapters, memory cards with - multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a struct pcmcia_device - structure. We allocate them in the card's private data structure, - because they generally can't be allocated dynamically. -*/ - -typedef struct local_info_t { - dev_node_t node; -} local_info_t; - /*====================================================================== avma1cs_attach() creates an "instance" of the driver, allocating @@ -101,17 +76,8 @@ typedef struct local_info_t { static int __devinit avma1cs_probe(struct pcmcia_device *p_dev) { - local_info_t *local; - dev_dbg(&p_dev->dev, "avma1cs_attach()\n"); - /* Allocate space for private device-specific data */ - local = kzalloc(sizeof(local_info_t), GFP_KERNEL); - if (!local) - return -ENOMEM; - - p_dev->priv = local; - /* The io structure describes IO port mapping */ p_dev->io.NumPorts1 = 16; p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; @@ -119,9 +85,6 @@ static int __devinit avma1cs_probe(struct pcmcia_device *p_dev) p_dev->io.Attributes2 = IO_DATA_PATH_WIDTH_16; p_dev->io.IOAddrLines = 5; - /* Interrupt setup */ - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - /* General socket configuration */ p_dev->conf.Attributes = CONF_ENABLE_IRQ; p_dev->conf.IntType = INT_MEMORY_AND_IO; @@ -176,14 +139,11 @@ static int avma1cs_configcheck(struct pcmcia_device *p_dev, static int __devinit avma1cs_config(struct pcmcia_device *link) { - local_info_t *dev; - int i; + int i = -1; char devname[128]; IsdnCard_t icard; int busy = 0; - dev = link->priv; - dev_dbg(&link->dev, "avma1cs_config(0x%p)\n", link); devname[0] = 0; @@ -197,8 +157,7 @@ static int __devinit avma1cs_config(struct pcmcia_device *link) /* * allocate an interrupt line */ - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { + if (!link->irq) { /* undo */ pcmcia_disable_device(link); break; @@ -215,14 +174,6 @@ static int __devinit avma1cs_config(struct pcmcia_device *link) } while (0); - /* At this point, the dev_node_t structure(s) should be - initialized and arranged in a linked list at link->dev. */ - - strcpy(dev->node.dev_name, "A1"); - dev->node.major = 45; - dev->node.minor = 0; - link->dev_node = &dev->node; - /* If any step failed, release any partially configured state */ if (i != 0) { avma1cs_release(link); @@ -230,9 +181,9 @@ static int __devinit avma1cs_config(struct pcmcia_device *link) } printk(KERN_NOTICE "avma1_cs: checking at i/o %#x, irq %d\n", - link->io.BasePort1, link->irq.AssignedIRQ); + link->io.BasePort1, link->irq); - icard.para[0] = link->irq.AssignedIRQ; + icard.para[0] = link->irq; icard.para[1] = link->io.BasePort1; icard.protocol = isdnprot; icard.typ = ISDN_CTYPE_A1_PCMCIA; @@ -243,7 +194,7 @@ static int __devinit avma1cs_config(struct pcmcia_device *link) avma1cs_release(link); return -ENODEV; } - dev->node.minor = i; + link->priv = (void *) (unsigned long) i; return 0; } /* avma1cs_config */ @@ -258,12 +209,12 @@ static int __devinit avma1cs_config(struct pcmcia_device *link) static void avma1cs_release(struct pcmcia_device *link) { - local_info_t *local = link->priv; + unsigned long minor = (unsigned long) link->priv; dev_dbg(&link->dev, "avma1cs_release(0x%p)\n", link); /* now unregister function with hisax */ - HiSax_closecard(local->node.minor); + HiSax_closecard(minor); pcmcia_disable_device(link); } /* avma1cs_release */ diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c index c9f2279e21f5..218927e3a4ea 100644 --- a/drivers/isdn/hisax/elsa_cs.c +++ b/drivers/isdn/hisax/elsa_cs.c @@ -87,24 +87,8 @@ static void elsa_cs_release(struct pcmcia_device *link); static void elsa_cs_detach(struct pcmcia_device *p_dev) __devexit; -/* - A driver needs to provide a dev_node_t structure for each device - on a card. In some cases, there is only one device per card (for - example, ethernet cards, modems). In other cases, there may be - many actual or logical devices (SCSI adapters, memory cards with - multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a struct pcmcia_device - structure. We allocate them in the card's private data structure, - because they generally shouldn't be allocated dynamically. - In this case, we also provide a flag to indicate if a device is - "stopped" due to a power management event, or card ejection. The - device IO routines can use a flag like this to throttle IO to a - card that is not ready to accept it. -*/ - typedef struct local_info_t { struct pcmcia_device *p_dev; - dev_node_t node; int busy; int cardnr; } local_info_t; @@ -136,10 +120,6 @@ static int __devinit elsa_cs_probe(struct pcmcia_device *link) local->cardnr = -1; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -223,28 +203,18 @@ static int __devinit elsa_cs_config(struct pcmcia_device *link) if (i != 0) goto failed; - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { - link->irq.AssignedIRQ = 0; + if (!link->irq) goto failed; - } i = pcmcia_request_configuration(link, &link->conf); if (i != 0) goto failed; - /* At this point, the dev_node_t structure(s) should be - initialized and arranged in a linked list at link->dev. *//* */ - sprintf(dev->node.dev_name, "elsa"); - dev->node.major = dev->node.minor = 0x0; - - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x: ", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x: ", + link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); @@ -253,7 +223,7 @@ static int __devinit elsa_cs_config(struct pcmcia_device *link) link->io.BasePort2+link->io.NumPorts2-1); printk("\n"); - icard.para[0] = link->irq.AssignedIRQ; + icard.para[0] = link->irq; icard.para[1] = link->io.BasePort1; icard.protocol = protocol; icard.typ = ISDN_CTYPE_ELSA_PCMCIA; diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c index 051b44e2556c..384d5118e325 100644 --- a/drivers/isdn/hisax/hfc4s8s_l1.c +++ b/drivers/isdn/hisax/hfc4s8s_l1.c @@ -310,7 +310,7 @@ wait_busy(hfc4s8s_hw * a) /******************************************************/ /* function to read critical counter registers that */ -/* may be udpated by the chip during read */ +/* may be updated by the chip during read */ /******************************************************/ static u_char Read_hfc8_stable(hfc4s8s_hw * hw, int reg) diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c index 71b3ddef03bb..1f4feaab21af 100644 --- a/drivers/isdn/hisax/sedlbauer_cs.c +++ b/drivers/isdn/hisax/sedlbauer_cs.c @@ -87,32 +87,8 @@ static void sedlbauer_release(struct pcmcia_device *link); static void sedlbauer_detach(struct pcmcia_device *p_dev) __devexit; -/* - You'll also need to prototype all the functions that will actually - be used to talk to your device. See 'memory_cs' for a good example - of a fully self-sufficient driver; the other drivers rely more or - less on other parts of the kernel. -*/ - -/* - A driver needs to provide a dev_node_t structure for each device - on a card. In some cases, there is only one device per card (for - example, ethernet cards, modems). In other cases, there may be - many actual or logical devices (SCSI adapters, memory cards with - multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a struct pcmcia_device - structure. We allocate them in the card's private data structure, - because they generally shouldn't be allocated dynamically. - - In this case, we also provide a flag to indicate if a device is - "stopped" due to a power management event, or card ejection. The - device IO routines can use a flag like this to throttle IO to a - card that is not ready to accept it. -*/ - typedef struct local_info_t { struct pcmcia_device *p_dev; - dev_node_t node; int stop; int cardnr; } local_info_t; @@ -143,10 +119,6 @@ static int __devinit sedlbauer_probe(struct pcmcia_device *link) local->p_dev = link; link->priv = local; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -227,9 +199,7 @@ static int sedlbauer_config_check(struct pcmcia_device *p_dev, else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM)) p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; - /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -285,7 +255,6 @@ static int sedlbauer_config_check(struct pcmcia_device *p_dev, static int __devinit sedlbauer_config(struct pcmcia_device *link) { - local_info_t *dev = link->priv; win_req_t *req; int ret; IsdnCard_t icard; @@ -313,17 +282,6 @@ static int __devinit sedlbauer_config(struct pcmcia_device *link) goto failed; /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } - - /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. @@ -332,21 +290,13 @@ static int __devinit sedlbauer_config(struct pcmcia_device *link) if (ret) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - sprintf(dev->node.dev_name, "sedlbauer"); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x:", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x:", + link->conf.ConfigIndex); if (link->conf.Vpp) printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); @@ -358,7 +308,7 @@ static int __devinit sedlbauer_config(struct pcmcia_device *link) req->Base+req->Size-1); printk("\n"); - icard.para[0] = link->irq.AssignedIRQ; + icard.para[0] = link->irq; icard.para[1] = link->io.BasePort1; icard.protocol = protocol; icard.typ = ISDN_CTYPE_SEDLBAUER_PCMCIA; diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c index d010a0da8e19..5771955cc532 100644 --- a/drivers/isdn/hisax/teles_cs.c +++ b/drivers/isdn/hisax/teles_cs.c @@ -68,34 +68,8 @@ static void teles_cs_release(struct pcmcia_device *link); static void teles_detach(struct pcmcia_device *p_dev) __devexit ; -/* - A linked list of "instances" of the teles_cs device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one struct pcmcia_device structure (defined in ds.h). - - You may not want to use a linked list for this -- for example, the - memory card driver uses an array of struct pcmcia_device pointers, where minor - device numbers are used to derive the corresponding array index. -*/ - -/* - A driver needs to provide a dev_node_t structure for each device - on a card. In some cases, there is only one device per card (for - example, ethernet cards, modems). In other cases, there may be - many actual or logical devices (SCSI adapters, memory cards with - multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a struct pcmcia_device - structure. We allocate them in the card's private data structure, - because they generally shouldn't be allocated dynamically. - In this case, we also provide a flag to indicate if a device is - "stopped" due to a power management event, or card ejection. The - device IO routines can use a flag like this to throttle IO to a - card that is not ready to accept it. -*/ - typedef struct local_info_t { struct pcmcia_device *p_dev; - dev_node_t node; int busy; int cardnr; } local_info_t; @@ -126,10 +100,6 @@ static int __devinit teles_probe(struct pcmcia_device *link) local->p_dev = link; link->priv = local; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -213,28 +183,18 @@ static int __devinit teles_cs_config(struct pcmcia_device *link) if (i != 0) goto cs_failed; - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { - link->irq.AssignedIRQ = 0; + if (!link->irq) goto cs_failed; - } i = pcmcia_request_configuration(link, &link->conf); if (i != 0) goto cs_failed; - /* At this point, the dev_node_t structure(s) should be - initialized and arranged in a linked list at link->dev. *//* */ - sprintf(dev->node.dev_name, "teles"); - dev->node.major = dev->node.minor = 0x0; - - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x:", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x:", + link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); @@ -243,7 +203,7 @@ static int __devinit teles_cs_config(struct pcmcia_device *link) link->io.BasePort2+link->io.NumPorts2-1); printk("\n"); - icard.para[0] = link->irq.AssignedIRQ; + icard.para[0] = link->irq; icard.para[1] = link->io.BasePort1; icard.protocol = protocol; icard.typ = ISDN_CTYPE_TELESPCMCIA; diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index 565d5b2adc95..129cda737880 100644 --- a/drivers/macintosh/windfarm_pm81.c +++ b/drivers/macintosh/windfarm_pm81.c @@ -188,7 +188,7 @@ struct wf_smu_sys_fans_state { }; /* - * Configs for SMU Sytem Fan control loop + * Configs for SMU System Fan control loop */ static struct wf_smu_sys_fans_param wf_smu_sys_all_params[] = { /* Model ID 2 */ diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig index 4dde7d180a32..195c6cf359f6 100644 --- a/drivers/media/IR/Kconfig +++ b/drivers/media/IR/Kconfig @@ -7,3 +7,62 @@ config VIDEO_IR tristate depends on IR_CORE default IR_CORE + +source "drivers/media/IR/keymaps/Kconfig" + +config IR_NEC_DECODER + tristate "Enable IR raw decoder for the NEC protocol" + depends on IR_CORE + default y + + ---help--- + Enable this option if you have IR with NEC protocol, and + if the IR is decoded in software + +config IR_RC5_DECODER + tristate "Enable IR raw decoder for the RC-5 protocol" + depends on IR_CORE + default y + + ---help--- + Enable this option if you have IR with RC-5 protocol, and + if the IR is decoded in software + +config IR_RC6_DECODER + tristate "Enable IR raw decoder for the RC6 protocol" + depends on IR_CORE + default y + + ---help--- + Enable this option if you have an infrared remote control which + uses the RC6 protocol, and you need software decoding support. + +config IR_JVC_DECODER + tristate "Enable IR raw decoder for the JVC protocol" + depends on IR_CORE + default y + + ---help--- + Enable this option if you have an infrared remote control which + uses the JVC protocol, and you need software decoding support. + +config IR_SONY_DECODER + tristate "Enable IR raw decoder for the Sony protocol" + depends on IR_CORE + default y + + ---help--- + Enable this option if you have an infrared remote control which + uses the Sony protocol, and you need software decoding support. + +config IR_IMON + tristate "SoundGraph iMON Receiver and Display" + depends on USB_ARCH_HAS_HCD + depends on IR_CORE + select USB + ---help--- + Say Y here if you want to use a SoundGraph iMON (aka Antec Veris) + IR Receiver and/or LCD/VFD/VGA display. + + To compile this driver as a module, choose M here: the + module will be called imon. diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile index 171890e7a41d..b998fcced2e4 100644 --- a/drivers/media/IR/Makefile +++ b/drivers/media/IR/Makefile @@ -1,5 +1,15 @@ -ir-common-objs := ir-functions.o ir-keymaps.o -ir-core-objs := ir-keytable.o ir-sysfs.o +ir-common-objs := ir-functions.o +ir-core-objs := ir-keytable.o ir-sysfs.o ir-raw-event.o rc-map.o + +obj-y += keymaps/ obj-$(CONFIG_IR_CORE) += ir-core.o obj-$(CONFIG_VIDEO_IR) += ir-common.o +obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o +obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o +obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o +obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o +obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o + +# stand-alone IR receivers/transmitters +obj-$(CONFIG_IR_IMON) += imon.o diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c new file mode 100644 index 000000000000..5e2045670004 --- /dev/null +++ b/drivers/media/IR/imon.c @@ -0,0 +1,2396 @@ +/* + * imon.c: input and display driver for SoundGraph iMON IR/VFD/LCD + * + * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com> + * Portions based on the original lirc_imon driver, + * Copyright(C) 2004 Venky Raju(dev@venky.ws) + * + * Huge thanks to R. Geoff Newbury for invaluable debugging on the + * 0xffdc iMON devices, and for sending me one to hack on, without + * which the support for them wouldn't be nearly as good. Thanks + * also to the numerous 0xffdc device owners that tested auto-config + * support for me and provided debug dumps from their devices. + * + * imon 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <linux/input.h> +#include <linux/usb.h> +#include <linux/usb/input.h> +#include <media/ir-core.h> + +#include <linux/time.h> +#include <linux/timer.h> + +#define MOD_AUTHOR "Jarod Wilson <jarod@wilsonet.com>" +#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display" +#define MOD_NAME "imon" +#define MOD_VERSION "0.9.1" + +#define DISPLAY_MINOR_BASE 144 +#define DEVICE_NAME "lcd%d" + +#define BUF_CHUNK_SIZE 8 +#define BUF_SIZE 128 + +#define BIT_DURATION 250 /* each bit received is 250us */ + +#define IMON_CLOCK_ENABLE_PACKETS 2 + +/*** P R O T O T Y P E S ***/ + +/* USB Callback prototypes */ +static int imon_probe(struct usb_interface *interface, + const struct usb_device_id *id); +static void imon_disconnect(struct usb_interface *interface); +static void usb_rx_callback_intf0(struct urb *urb); +static void usb_rx_callback_intf1(struct urb *urb); +static void usb_tx_callback(struct urb *urb); + +/* suspend/resume support */ +static int imon_resume(struct usb_interface *intf); +static int imon_suspend(struct usb_interface *intf, pm_message_t message); + +/* Display file_operations function prototypes */ +static int display_open(struct inode *inode, struct file *file); +static int display_close(struct inode *inode, struct file *file); + +/* VFD write operation */ +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos); + +/* LCD file_operations override function prototypes */ +static ssize_t lcd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos); + +/*** G L O B A L S ***/ + +struct imon_context { + struct device *dev; + struct ir_dev_props *props; + struct ir_input_dev *ir; + /* Newer devices have two interfaces */ + struct usb_device *usbdev_intf0; + struct usb_device *usbdev_intf1; + + bool display_supported; /* not all controllers do */ + bool display_isopen; /* display port has been opened */ + bool rf_isassociating; /* RF remote associating */ + bool dev_present_intf0; /* USB device presence, interface 0 */ + bool dev_present_intf1; /* USB device presence, interface 1 */ + + struct mutex lock; /* to lock this object */ + wait_queue_head_t remove_ok; /* For unexpected USB disconnects */ + + struct usb_endpoint_descriptor *rx_endpoint_intf0; + struct usb_endpoint_descriptor *rx_endpoint_intf1; + struct usb_endpoint_descriptor *tx_endpoint; + struct urb *rx_urb_intf0; + struct urb *rx_urb_intf1; + struct urb *tx_urb; + bool tx_control; + unsigned char usb_rx_buf[8]; + unsigned char usb_tx_buf[8]; + + struct tx_t { + unsigned char data_buf[35]; /* user data buffer */ + struct completion finished; /* wait for write to finish */ + bool busy; /* write in progress */ + int status; /* status of tx completion */ + } tx; + + u16 vendor; /* usb vendor ID */ + u16 product; /* usb product ID */ + + struct input_dev *idev; /* input device for remote */ + struct input_dev *touch; /* input device for touchscreen */ + + u32 kc; /* current input keycode */ + u32 last_keycode; /* last reported input keycode */ + u64 ir_type; /* iMON or MCE (RC6) IR protocol? */ + u8 mce_toggle_bit; /* last mce toggle bit */ + bool release_code; /* some keys send a release code */ + + u8 display_type; /* store the display type */ + bool pad_mouse; /* toggle kbd(0)/mouse(1) mode */ + + char name_idev[128]; /* input device name */ + char phys_idev[64]; /* input device phys path */ + struct timer_list itimer; /* input device timer, need for rc6 */ + + char name_touch[128]; /* touch screen name */ + char phys_touch[64]; /* touch screen phys path */ + struct timer_list ttimer; /* touch screen timer */ + int touch_x; /* x coordinate on touchscreen */ + int touch_y; /* y coordinate on touchscreen */ +}; + +#define TOUCH_TIMEOUT (HZ/30) + +/* vfd character device file operations */ +static const struct file_operations vfd_fops = { + .owner = THIS_MODULE, + .open = &display_open, + .write = &vfd_write, + .release = &display_close +}; + +/* lcd character device file operations */ +static const struct file_operations lcd_fops = { + .owner = THIS_MODULE, + .open = &display_open, + .write = &lcd_write, + .release = &display_close +}; + +enum { + IMON_DISPLAY_TYPE_AUTO = 0, + IMON_DISPLAY_TYPE_VFD = 1, + IMON_DISPLAY_TYPE_LCD = 2, + IMON_DISPLAY_TYPE_VGA = 3, + IMON_DISPLAY_TYPE_NONE = 4, +}; + +enum { + IMON_KEY_IMON = 0, + IMON_KEY_MCE = 1, + IMON_KEY_PANEL = 2, +}; + +/* + * USB Device ID for iMON USB Control Boards + * + * The Windows drivers contain 6 different inf files, more or less one for + * each new device until the 0x0034-0x0046 devices, which all use the same + * driver. Some of the devices in the 34-46 range haven't been definitively + * identified yet. Early devices have either a TriGem Computer, Inc. or a + * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later + * devices use the SoundGraph vendor ID (0x15c2). This driver only supports + * the ffdc and later devices, which do onboard decoding. + */ +static struct usb_device_id imon_usb_id_table[] = { + /* + * Several devices with this same device ID, all use iMON_PAD.inf + * SoundGraph iMON PAD (IR & VFD) + * SoundGraph iMON PAD (IR & LCD) + * SoundGraph iMON Knob (IR only) + */ + { USB_DEVICE(0x15c2, 0xffdc) }, + + /* + * Newer devices, all driven by the latest iMON Windows driver, full + * list of device IDs extracted via 'strings Setup/data1.hdr |grep 15c2' + * Need user input to fill in details on unknown devices. + */ + /* SoundGraph iMON OEM Touch LCD (IR & 7" VGA LCD) */ + { USB_DEVICE(0x15c2, 0x0034) }, + /* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */ + { USB_DEVICE(0x15c2, 0x0035) }, + /* SoundGraph iMON OEM VFD (IR & VFD) */ + { USB_DEVICE(0x15c2, 0x0036) }, + /* device specifics unknown */ + { USB_DEVICE(0x15c2, 0x0037) }, + /* SoundGraph iMON OEM LCD (IR & LCD) */ + { USB_DEVICE(0x15c2, 0x0038) }, + /* SoundGraph iMON UltraBay (IR & LCD) */ + { USB_DEVICE(0x15c2, 0x0039) }, + /* device specifics unknown */ + { USB_DEVICE(0x15c2, 0x003a) }, + /* device specifics unknown */ + { USB_DEVICE(0x15c2, 0x003b) }, + /* SoundGraph iMON OEM Inside (IR only) */ + { USB_DEVICE(0x15c2, 0x003c) }, + /* device specifics unknown */ + { USB_DEVICE(0x15c2, 0x003d) }, + /* device specifics unknown */ + { USB_DEVICE(0x15c2, 0x003e) }, + /* device specifics unknown */ + { USB_DEVICE(0x15c2, 0x003f) }, + /* device specifics unknown */ + { USB_DEVICE(0x15c2, 0x0040) }, + /* SoundGraph iMON MINI (IR only) */ + { USB_DEVICE(0x15c2, 0x0041) }, + /* Antec Veris Multimedia Station EZ External (IR only) */ + { USB_DEVICE(0x15c2, 0x0042) }, + /* Antec Veris Multimedia Station Basic Internal (IR only) */ + { USB_DEVICE(0x15c2, 0x0043) }, + /* Antec Veris Multimedia Station Elite (IR & VFD) */ + { USB_DEVICE(0x15c2, 0x0044) }, + /* Antec Veris Multimedia Station Premiere (IR & LCD) */ + { USB_DEVICE(0x15c2, 0x0045) }, + /* device specifics unknown */ + { USB_DEVICE(0x15c2, 0x0046) }, + {} +}; + +/* USB Device data */ +static struct usb_driver imon_driver = { + .name = MOD_NAME, + .probe = imon_probe, + .disconnect = imon_disconnect, + .suspend = imon_suspend, + .resume = imon_resume, + .id_table = imon_usb_id_table, +}; + +static struct usb_class_driver imon_vfd_class = { + .name = DEVICE_NAME, + .fops = &vfd_fops, + .minor_base = DISPLAY_MINOR_BASE, +}; + +static struct usb_class_driver imon_lcd_class = { + .name = DEVICE_NAME, + .fops = &lcd_fops, + .minor_base = DISPLAY_MINOR_BASE, +}; + +/* imon receiver front panel/knob key table */ +static const struct { + u64 hw_code; + u32 keycode; +} imon_panel_key_table[] = { + { 0x000000000f00ffeell, KEY_PROG1 }, /* Go */ + { 0x000000001f00ffeell, KEY_AUDIO }, + { 0x000000002000ffeell, KEY_VIDEO }, + { 0x000000002100ffeell, KEY_CAMERA }, + { 0x000000002700ffeell, KEY_DVD }, + { 0x000000002300ffeell, KEY_TV }, + { 0x000000000500ffeell, KEY_PREVIOUS }, + { 0x000000000700ffeell, KEY_REWIND }, + { 0x000000000400ffeell, KEY_STOP }, + { 0x000000003c00ffeell, KEY_PLAYPAUSE }, + { 0x000000000800ffeell, KEY_FASTFORWARD }, + { 0x000000000600ffeell, KEY_NEXT }, + { 0x000000010000ffeell, KEY_RIGHT }, + { 0x000001000000ffeell, KEY_LEFT }, + { 0x000000003d00ffeell, KEY_SELECT }, + { 0x000100000000ffeell, KEY_VOLUMEUP }, + { 0x010000000000ffeell, KEY_VOLUMEDOWN }, + { 0x000000000100ffeell, KEY_MUTE }, + /* iMON Knob values */ + { 0x000100ffffffffeell, KEY_VOLUMEUP }, + { 0x010000ffffffffeell, KEY_VOLUMEDOWN }, + { 0x000008ffffffffeell, KEY_MUTE }, +}; + +/* to prevent races between open() and disconnect(), probing, etc */ +static DEFINE_MUTEX(driver_lock); + +/* Module bookkeeping bits */ +MODULE_AUTHOR(MOD_AUTHOR); +MODULE_DESCRIPTION(MOD_DESC); +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, imon_usb_id_table); + +static bool debug; +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)"); + +/* lcd, vfd, vga or none? should be auto-detected, but can be overridden... */ +static int display_type; +module_param(display_type, int, S_IRUGO); +MODULE_PARM_DESC(display_type, "Type of attached display. 0=autodetect, " + "1=vfd, 2=lcd, 3=vga, 4=none (default: autodetect)"); + +static int pad_stabilize = 1; +module_param(pad_stabilize, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(pad_stabilize, "Apply stabilization algorithm to iMON PAD " + "presses in arrow key mode. 0=disable, 1=enable (default)."); + +/* + * In certain use cases, mouse mode isn't really helpful, and could actually + * cause confusion, so allow disabling it when the IR device is open. + */ +static bool nomouse; +module_param(nomouse, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(nomouse, "Disable mouse input device mode when IR device is " + "open. 0=don't disable, 1=disable. (default: don't disable)"); + +/* threshold at which a pad push registers as an arrow key in kbd mode */ +static int pad_thresh; +module_param(pad_thresh, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(pad_thresh, "Threshold at which a pad push registers as an " + "arrow key in kbd mode (default: 28)"); + + +static void free_imon_context(struct imon_context *ictx) +{ + struct device *dev = ictx->dev; + + usb_free_urb(ictx->tx_urb); + usb_free_urb(ictx->rx_urb_intf0); + usb_free_urb(ictx->rx_urb_intf1); + kfree(ictx); + + dev_dbg(dev, "%s: iMON context freed\n", __func__); +} + +/** + * Called when the Display device (e.g. /dev/lcd0) + * is opened by the application. + */ +static int display_open(struct inode *inode, struct file *file) +{ + struct usb_interface *interface; + struct imon_context *ictx = NULL; + int subminor; + int retval = 0; + + /* prevent races with disconnect */ + mutex_lock(&driver_lock); + + subminor = iminor(inode); + interface = usb_find_interface(&imon_driver, subminor); + if (!interface) { + err("%s: could not find interface for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + ictx = usb_get_intfdata(interface); + + if (!ictx) { + err("%s: no context found for minor %d", __func__, subminor); + retval = -ENODEV; + goto exit; + } + + mutex_lock(&ictx->lock); + + if (!ictx->display_supported) { + err("%s: display not supported by device", __func__); + retval = -ENODEV; + } else if (ictx->display_isopen) { + err("%s: display port is already open", __func__); + retval = -EBUSY; + } else { + ictx->display_isopen = 1; + file->private_data = ictx; + dev_dbg(ictx->dev, "display port opened\n"); + } + + mutex_unlock(&ictx->lock); + +exit: + mutex_unlock(&driver_lock); + return retval; +} + +/** + * Called when the display device (e.g. /dev/lcd0) + * is closed by the application. + */ +static int display_close(struct inode *inode, struct file *file) +{ + struct imon_context *ictx = NULL; + int retval = 0; + + ictx = (struct imon_context *)file->private_data; + + if (!ictx) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&ictx->lock); + + if (!ictx->display_supported) { + err("%s: display not supported by device", __func__); + retval = -ENODEV; + } else if (!ictx->display_isopen) { + err("%s: display is not open", __func__); + retval = -EIO; + } else { + ictx->display_isopen = 0; + dev_dbg(ictx->dev, "display port closed\n"); + if (!ictx->dev_present_intf0) { + /* + * Device disconnected before close and IR port is not + * open. If IR port is open, context will be deleted by + * ir_close. + */ + mutex_unlock(&ictx->lock); + free_imon_context(ictx); + return retval; + } + } + + mutex_unlock(&ictx->lock); + return retval; +} + +/** + * Sends a packet to the device -- this function must be called + * with ictx->lock held. + */ +static int send_packet(struct imon_context *ictx) +{ + unsigned int pipe; + unsigned long timeout; + int interval = 0; + int retval = 0; + struct usb_ctrlrequest *control_req = NULL; + + /* Check if we need to use control or interrupt urb */ + if (!ictx->tx_control) { + pipe = usb_sndintpipe(ictx->usbdev_intf0, + ictx->tx_endpoint->bEndpointAddress); + interval = ictx->tx_endpoint->bInterval; + + usb_fill_int_urb(ictx->tx_urb, ictx->usbdev_intf0, pipe, + ictx->usb_tx_buf, + sizeof(ictx->usb_tx_buf), + usb_tx_callback, ictx, interval); + + ictx->tx_urb->actual_length = 0; + } else { + /* fill request into kmalloc'ed space: */ + control_req = kmalloc(sizeof(struct usb_ctrlrequest), + GFP_KERNEL); + if (control_req == NULL) + return -ENOMEM; + + /* setup packet is '21 09 0200 0001 0008' */ + control_req->bRequestType = 0x21; + control_req->bRequest = 0x09; + control_req->wValue = cpu_to_le16(0x0200); + control_req->wIndex = cpu_to_le16(0x0001); + control_req->wLength = cpu_to_le16(0x0008); + + /* control pipe is endpoint 0x00 */ + pipe = usb_sndctrlpipe(ictx->usbdev_intf0, 0); + + /* build the control urb */ + usb_fill_control_urb(ictx->tx_urb, ictx->usbdev_intf0, + pipe, (unsigned char *)control_req, + ictx->usb_tx_buf, + sizeof(ictx->usb_tx_buf), + usb_tx_callback, ictx); + ictx->tx_urb->actual_length = 0; + } + + init_completion(&ictx->tx.finished); + ictx->tx.busy = 1; + smp_rmb(); /* ensure later readers know we're busy */ + + retval = usb_submit_urb(ictx->tx_urb, GFP_KERNEL); + if (retval) { + ictx->tx.busy = 0; + smp_rmb(); /* ensure later readers know we're not busy */ + err("%s: error submitting urb(%d)", __func__, retval); + } else { + /* Wait for transmission to complete (or abort) */ + mutex_unlock(&ictx->lock); + retval = wait_for_completion_interruptible( + &ictx->tx.finished); + if (retval) + err("%s: task interrupted", __func__); + mutex_lock(&ictx->lock); + + retval = ictx->tx.status; + if (retval) + err("%s: packet tx failed (%d)", __func__, retval); + } + + kfree(control_req); + + /* + * Induce a mandatory 5ms delay before returning, as otherwise, + * send_packet can get called so rapidly as to overwhelm the device, + * particularly on faster systems and/or those with quirky usb. + */ + timeout = msecs_to_jiffies(5); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout); + + return retval; +} + +/** + * Sends an associate packet to the iMON 2.4G. + * + * This might not be such a good idea, since it has an id collision with + * some versions of the "IR & VFD" combo. The only way to determine if it + * is an RF version is to look at the product description string. (Which + * we currently do not fetch). + */ +static int send_associate_24g(struct imon_context *ictx) +{ + int retval; + const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20 }; + + if (!ictx) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + if (!ictx->dev_present_intf0) { + err("%s: no iMON device present", __func__); + return -ENODEV; + } + + memcpy(ictx->usb_tx_buf, packet, sizeof(packet)); + retval = send_packet(ictx); + + return retval; +} + +/** + * Sends packets to setup and show clock on iMON display + * + * Arguments: year - last 2 digits of year, month - 1..12, + * day - 1..31, dow - day of the week (0-Sun...6-Sat), + * hour - 0..23, minute - 0..59, second - 0..59 + */ +static int send_set_imon_clock(struct imon_context *ictx, + unsigned int year, unsigned int month, + unsigned int day, unsigned int dow, + unsigned int hour, unsigned int minute, + unsigned int second) +{ + unsigned char clock_enable_pkt[IMON_CLOCK_ENABLE_PACKETS][8]; + int retval = 0; + int i; + + if (!ictx) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + switch (ictx->display_type) { + case IMON_DISPLAY_TYPE_LCD: + clock_enable_pkt[0][0] = 0x80; + clock_enable_pkt[0][1] = year; + clock_enable_pkt[0][2] = month-1; + clock_enable_pkt[0][3] = day; + clock_enable_pkt[0][4] = hour; + clock_enable_pkt[0][5] = minute; + clock_enable_pkt[0][6] = second; + + clock_enable_pkt[1][0] = 0x80; + clock_enable_pkt[1][1] = 0; + clock_enable_pkt[1][2] = 0; + clock_enable_pkt[1][3] = 0; + clock_enable_pkt[1][4] = 0; + clock_enable_pkt[1][5] = 0; + clock_enable_pkt[1][6] = 0; + + if (ictx->product == 0xffdc) { + clock_enable_pkt[0][7] = 0x50; + clock_enable_pkt[1][7] = 0x51; + } else { + clock_enable_pkt[0][7] = 0x88; + clock_enable_pkt[1][7] = 0x8a; + } + + break; + + case IMON_DISPLAY_TYPE_VFD: + clock_enable_pkt[0][0] = year; + clock_enable_pkt[0][1] = month-1; + clock_enable_pkt[0][2] = day; + clock_enable_pkt[0][3] = dow; + clock_enable_pkt[0][4] = hour; + clock_enable_pkt[0][5] = minute; + clock_enable_pkt[0][6] = second; + clock_enable_pkt[0][7] = 0x40; + + clock_enable_pkt[1][0] = 0; + clock_enable_pkt[1][1] = 0; + clock_enable_pkt[1][2] = 1; + clock_enable_pkt[1][3] = 0; + clock_enable_pkt[1][4] = 0; + clock_enable_pkt[1][5] = 0; + clock_enable_pkt[1][6] = 0; + clock_enable_pkt[1][7] = 0x42; + + break; + + default: + return -ENODEV; + } + + for (i = 0; i < IMON_CLOCK_ENABLE_PACKETS; i++) { + memcpy(ictx->usb_tx_buf, clock_enable_pkt[i], 8); + retval = send_packet(ictx); + if (retval) { + err("%s: send_packet failed for packet %d", + __func__, i); + break; + } + } + + return retval; +} + +/** + * These are the sysfs functions to handle the association on the iMON 2.4G LT. + */ +static ssize_t show_associate_remote(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct imon_context *ictx = dev_get_drvdata(d); + + if (!ictx) + return -ENODEV; + + mutex_lock(&ictx->lock); + if (ictx->rf_isassociating) + strcpy(buf, "associating\n"); + else + strcpy(buf, "closed\n"); + + dev_info(d, "Visit http://www.lirc.org/html/imon-24g.html for " + "instructions on how to associate your iMON 2.4G DT/LT " + "remote\n"); + mutex_unlock(&ictx->lock); + return strlen(buf); +} + +static ssize_t store_associate_remote(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imon_context *ictx; + + ictx = dev_get_drvdata(d); + + if (!ictx) + return -ENODEV; + + mutex_lock(&ictx->lock); + ictx->rf_isassociating = 1; + send_associate_24g(ictx); + mutex_unlock(&ictx->lock); + + return count; +} + +/** + * sysfs functions to control internal imon clock + */ +static ssize_t show_imon_clock(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct imon_context *ictx = dev_get_drvdata(d); + size_t len; + + if (!ictx) + return -ENODEV; + + mutex_lock(&ictx->lock); + + if (!ictx->display_supported) { + len = snprintf(buf, PAGE_SIZE, "Not supported."); + } else { + len = snprintf(buf, PAGE_SIZE, + "To set the clock on your iMON display:\n" + "# date \"+%%y %%m %%d %%w %%H %%M %%S\" > imon_clock\n" + "%s", ictx->display_isopen ? + "\nNOTE: imon device must be closed\n" : ""); + } + + mutex_unlock(&ictx->lock); + + return len; +} + +static ssize_t store_imon_clock(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct imon_context *ictx = dev_get_drvdata(d); + ssize_t retval; + unsigned int year, month, day, dow, hour, minute, second; + + if (!ictx) + return -ENODEV; + + mutex_lock(&ictx->lock); + + if (!ictx->display_supported) { + retval = -ENODEV; + goto exit; + } else if (ictx->display_isopen) { + retval = -EBUSY; + goto exit; + } + + if (sscanf(buf, "%u %u %u %u %u %u %u", &year, &month, &day, &dow, + &hour, &minute, &second) != 7) { + retval = -EINVAL; + goto exit; + } + + if ((month < 1 || month > 12) || + (day < 1 || day > 31) || (dow > 6) || + (hour > 23) || (minute > 59) || (second > 59)) { + retval = -EINVAL; + goto exit; + } + + retval = send_set_imon_clock(ictx, year, month, day, dow, + hour, minute, second); + if (retval) + goto exit; + + retval = count; +exit: + mutex_unlock(&ictx->lock); + + return retval; +} + + +static DEVICE_ATTR(imon_clock, S_IWUSR | S_IRUGO, show_imon_clock, + store_imon_clock); + +static DEVICE_ATTR(associate_remote, S_IWUSR | S_IRUGO, show_associate_remote, + store_associate_remote); + +static struct attribute *imon_display_sysfs_entries[] = { + &dev_attr_imon_clock.attr, + NULL +}; + +static struct attribute_group imon_display_attribute_group = { + .attrs = imon_display_sysfs_entries +}; + +static struct attribute *imon_rf_sysfs_entries[] = { + &dev_attr_associate_remote.attr, + NULL +}; + +static struct attribute_group imon_rf_attribute_group = { + .attrs = imon_rf_sysfs_entries +}; + +/** + * Writes data to the VFD. The iMON VFD is 2x16 characters + * and requires data in 5 consecutive USB interrupt packets, + * each packet but the last carrying 7 bytes. + * + * I don't know if the VFD board supports features such as + * scrolling, clearing rows, blanking, etc. so at + * the caller must provide a full screen of data. If fewer + * than 32 bytes are provided spaces will be appended to + * generate a full screen. + */ +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos) +{ + int i; + int offset; + int seq; + int retval = 0; + struct imon_context *ictx; + const unsigned char vfd_packet6[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; + + ictx = (struct imon_context *)file->private_data; + if (!ictx) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&ictx->lock); + + if (!ictx->dev_present_intf0) { + err("%s: no iMON device present", __func__); + retval = -ENODEV; + goto exit; + } + + if (n_bytes <= 0 || n_bytes > 32) { + err("%s: invalid payload size", __func__); + retval = -EINVAL; + goto exit; + } + + if (copy_from_user(ictx->tx.data_buf, buf, n_bytes)) { + retval = -EFAULT; + goto exit; + } + + /* Pad with spaces */ + for (i = n_bytes; i < 32; ++i) + ictx->tx.data_buf[i] = ' '; + + for (i = 32; i < 35; ++i) + ictx->tx.data_buf[i] = 0xFF; + + offset = 0; + seq = 0; + + do { + memcpy(ictx->usb_tx_buf, ictx->tx.data_buf + offset, 7); + ictx->usb_tx_buf[7] = (unsigned char) seq; + + retval = send_packet(ictx); + if (retval) { + err("%s: send packet failed for packet #%d", + __func__, seq/2); + goto exit; + } else { + seq += 2; + offset += 7; + } + + } while (offset < 35); + + /* Send packet #6 */ + memcpy(ictx->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6)); + ictx->usb_tx_buf[7] = (unsigned char) seq; + retval = send_packet(ictx); + if (retval) + err("%s: send packet failed for packet #%d", + __func__, seq / 2); + +exit: + mutex_unlock(&ictx->lock); + + return (!retval) ? n_bytes : retval; +} + +/** + * Writes data to the LCD. The iMON OEM LCD screen expects 8-byte + * packets. We accept data as 16 hexadecimal digits, followed by a + * newline (to make it easy to drive the device from a command-line + * -- even though the actual binary data is a bit complicated). + * + * The device itself is not a "traditional" text-mode display. It's + * actually a 16x96 pixel bitmap display. That means if you want to + * display text, you've got to have your own "font" and translate the + * text into bitmaps for display. This is really flexible (you can + * display whatever diacritics you need, and so on), but it's also + * a lot more complicated than most LCDs... + */ +static ssize_t lcd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos) +{ + int retval = 0; + struct imon_context *ictx; + + ictx = (struct imon_context *)file->private_data; + if (!ictx) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&ictx->lock); + + if (!ictx->display_supported) { + err("%s: no iMON display present", __func__); + retval = -ENODEV; + goto exit; + } + + if (n_bytes != 8) { + err("%s: invalid payload size: %d (expecting 8)", + __func__, (int) n_bytes); + retval = -EINVAL; + goto exit; + } + + if (copy_from_user(ictx->usb_tx_buf, buf, 8)) { + retval = -EFAULT; + goto exit; + } + + retval = send_packet(ictx); + if (retval) { + err("%s: send packet failed!", __func__); + goto exit; + } else { + dev_dbg(ictx->dev, "%s: write %d bytes to LCD\n", + __func__, (int) n_bytes); + } +exit: + mutex_unlock(&ictx->lock); + return (!retval) ? n_bytes : retval; +} + +/** + * Callback function for USB core API: transmit data + */ +static void usb_tx_callback(struct urb *urb) +{ + struct imon_context *ictx; + + if (!urb) + return; + ictx = (struct imon_context *)urb->context; + if (!ictx) + return; + + ictx->tx.status = urb->status; + + /* notify waiters that write has finished */ + ictx->tx.busy = 0; + smp_rmb(); /* ensure later readers know we're not busy */ + complete(&ictx->tx.finished); +} + +/** + * mce/rc6 keypresses have no distinct release code, use timer + */ +static void imon_mce_timeout(unsigned long data) +{ + struct imon_context *ictx = (struct imon_context *)data; + + input_report_key(ictx->idev, ictx->last_keycode, 0); + input_sync(ictx->idev); +} + +/** + * report touchscreen input + */ +static void imon_touch_display_timeout(unsigned long data) +{ + struct imon_context *ictx = (struct imon_context *)data; + + if (ictx->display_type != IMON_DISPLAY_TYPE_VGA) + return; + + input_report_abs(ictx->touch, ABS_X, ictx->touch_x); + input_report_abs(ictx->touch, ABS_Y, ictx->touch_y); + input_report_key(ictx->touch, BTN_TOUCH, 0x00); + input_sync(ictx->touch); +} + +/** + * iMON IR receivers support two different signal sets -- those used by + * the iMON remotes, and those used by the Windows MCE remotes (which is + * really just RC-6), but only one or the other at a time, as the signals + * are decoded onboard the receiver. + */ +int imon_ir_change_protocol(void *priv, u64 ir_type) +{ + int retval; + struct imon_context *ictx = priv; + struct device *dev = ictx->dev; + bool pad_mouse; + unsigned char ir_proto_packet[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 }; + + if (ir_type && !(ir_type & ictx->props->allowed_protos)) + dev_warn(dev, "Looks like you're trying to use an IR protocol " + "this device does not support\n"); + + switch (ir_type) { + case IR_TYPE_RC6: + dev_dbg(dev, "Configuring IR receiver for MCE protocol\n"); + ir_proto_packet[0] = 0x01; + pad_mouse = false; + init_timer(&ictx->itimer); + ictx->itimer.data = (unsigned long)ictx; + ictx->itimer.function = imon_mce_timeout; + break; + case IR_TYPE_UNKNOWN: + case IR_TYPE_OTHER: + dev_dbg(dev, "Configuring IR receiver for iMON protocol\n"); + if (pad_stabilize) + pad_mouse = true; + else { + dev_dbg(dev, "PAD stabilize functionality disabled\n"); + pad_mouse = false; + } + /* ir_proto_packet[0] = 0x00; // already the default */ + ir_type = IR_TYPE_OTHER; + break; + default: + dev_warn(dev, "Unsupported IR protocol specified, overriding " + "to iMON IR protocol\n"); + if (pad_stabilize) + pad_mouse = true; + else { + dev_dbg(dev, "PAD stabilize functionality disabled\n"); + pad_mouse = false; + } + /* ir_proto_packet[0] = 0x00; // already the default */ + ir_type = IR_TYPE_OTHER; + break; + } + + memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet)); + + retval = send_packet(ictx); + if (retval) + goto out; + + ictx->ir_type = ir_type; + ictx->pad_mouse = pad_mouse; + +out: + return retval; +} + +static inline int tv2int(const struct timeval *a, const struct timeval *b) +{ + int usecs = 0; + int sec = 0; + + if (b->tv_usec > a->tv_usec) { + usecs = 1000000; + sec--; + } + + usecs += a->tv_usec - b->tv_usec; + + sec += a->tv_sec - b->tv_sec; + sec *= 1000; + usecs /= 1000; + sec += usecs; + + if (sec < 0) + sec = 1000; + + return sec; +} + +/** + * The directional pad behaves a bit differently, depending on whether this is + * one of the older ffdc devices or a newer device. Newer devices appear to + * have a higher resolution matrix for more precise mouse movement, but it + * makes things overly sensitive in keyboard mode, so we do some interesting + * contortions to make it less touchy. Older devices run through the same + * routine with shorter timeout and a smaller threshold. + */ +static int stabilize(int a, int b, u16 timeout, u16 threshold) +{ + struct timeval ct; + static struct timeval prev_time = {0, 0}; + static struct timeval hit_time = {0, 0}; + static int x, y, prev_result, hits; + int result = 0; + int msec, msec_hit; + + do_gettimeofday(&ct); + msec = tv2int(&ct, &prev_time); + msec_hit = tv2int(&ct, &hit_time); + + if (msec > 100) { + x = 0; + y = 0; + hits = 0; + } + + x += a; + y += b; + + prev_time = ct; + + if (abs(x) > threshold || abs(y) > threshold) { + if (abs(y) > abs(x)) + result = (y > 0) ? 0x7F : 0x80; + else + result = (x > 0) ? 0x7F00 : 0x8000; + + x = 0; + y = 0; + + if (result == prev_result) { + hits++; + + if (hits > 3) { + switch (result) { + case 0x7F: + y = 17 * threshold / 30; + break; + case 0x80: + y -= 17 * threshold / 30; + break; + case 0x7F00: + x = 17 * threshold / 30; + break; + case 0x8000: + x -= 17 * threshold / 30; + break; + } + } + + if (hits == 2 && msec_hit < timeout) { + result = 0; + hits = 1; + } + } else { + prev_result = result; + hits = 1; + hit_time = ct; + } + } + + return result; +} + +static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 hw_code) +{ + u32 scancode = be32_to_cpu(hw_code); + u32 keycode; + u32 release; + bool is_release_code = false; + + /* Look for the initial press of a button */ + keycode = ir_g_keycode_from_table(ictx->idev, scancode); + + /* Look for the release of a button */ + if (keycode == KEY_RESERVED) { + release = scancode & ~0x4000; + keycode = ir_g_keycode_from_table(ictx->idev, release); + if (keycode != KEY_RESERVED) + is_release_code = true; + } + + ictx->release_code = is_release_code; + + return keycode; +} + +static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 hw_code) +{ + u32 scancode = be32_to_cpu(hw_code); + u32 keycode; + +#define MCE_KEY_MASK 0x7000 +#define MCE_TOGGLE_BIT 0x8000 + + /* + * On some receivers, mce keys decode to 0x8000f04xx and 0x8000f84xx + * (the toggle bit flipping between alternating key presses), while + * on other receivers, we see 0x8000f74xx and 0x8000ff4xx. To keep + * the table trim, we always or in the bits to look up 0x8000ff4xx, + * but we can't or them into all codes, as some keys are decoded in + * a different way w/o the same use of the toggle bit... + */ + if ((scancode >> 24) & 0x80) + scancode = scancode | MCE_KEY_MASK | MCE_TOGGLE_BIT; + + keycode = ir_g_keycode_from_table(ictx->idev, scancode); + + return keycode; +} + +static u32 imon_panel_key_lookup(u64 hw_code) +{ + int i; + u64 code = be64_to_cpu(hw_code); + u32 keycode = KEY_RESERVED; + + for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) { + if (imon_panel_key_table[i].hw_code == (code | 0xffee)) { + keycode = imon_panel_key_table[i].keycode; + break; + } + } + + return keycode; +} + +static bool imon_mouse_event(struct imon_context *ictx, + unsigned char *buf, int len) +{ + char rel_x = 0x00, rel_y = 0x00; + u8 right_shift = 1; + bool mouse_input = 1; + int dir = 0; + + /* newer iMON device PAD or mouse button */ + if (ictx->product != 0xffdc && (buf[0] & 0x01) && len == 5) { + rel_x = buf[2]; + rel_y = buf[3]; + right_shift = 1; + /* 0xffdc iMON PAD or mouse button input */ + } else if (ictx->product == 0xffdc && (buf[0] & 0x40) && + !((buf[1] & 0x01) || ((buf[1] >> 2) & 0x01))) { + rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 | + (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6; + if (buf[0] & 0x02) + rel_x |= ~0x0f; + rel_x = rel_x + rel_x / 2; + rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 | + (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6; + if (buf[0] & 0x01) + rel_y |= ~0x0f; + rel_y = rel_y + rel_y / 2; + right_shift = 2; + /* some ffdc devices decode mouse buttons differently... */ + } else if (ictx->product == 0xffdc && (buf[0] == 0x68)) { + right_shift = 2; + /* ch+/- buttons, which we use for an emulated scroll wheel */ + } else if (ictx->kc == KEY_CHANNELUP && (buf[2] & 0x40) != 0x40) { + dir = 1; + } else if (ictx->kc == KEY_CHANNELDOWN && (buf[2] & 0x40) != 0x40) { + dir = -1; + } else + mouse_input = 0; + + if (mouse_input) { + dev_dbg(ictx->dev, "sending mouse data via input subsystem\n"); + + if (dir) { + input_report_rel(ictx->idev, REL_WHEEL, dir); + } else if (rel_x || rel_y) { + input_report_rel(ictx->idev, REL_X, rel_x); + input_report_rel(ictx->idev, REL_Y, rel_y); + } else { + input_report_key(ictx->idev, BTN_LEFT, buf[1] & 0x1); + input_report_key(ictx->idev, BTN_RIGHT, + buf[1] >> right_shift & 0x1); + } + input_sync(ictx->idev); + ictx->last_keycode = ictx->kc; + } + + return mouse_input; +} + +static void imon_touch_event(struct imon_context *ictx, unsigned char *buf) +{ + mod_timer(&ictx->ttimer, jiffies + TOUCH_TIMEOUT); + ictx->touch_x = (buf[0] << 4) | (buf[1] >> 4); + ictx->touch_y = 0xfff - ((buf[2] << 4) | (buf[1] & 0xf)); + input_report_abs(ictx->touch, ABS_X, ictx->touch_x); + input_report_abs(ictx->touch, ABS_Y, ictx->touch_y); + input_report_key(ictx->touch, BTN_TOUCH, 0x01); + input_sync(ictx->touch); +} + +static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf) +{ + int dir = 0; + char rel_x = 0x00, rel_y = 0x00; + u16 timeout, threshold; + u64 temp_key; + u32 remote_key; + + /* + * The imon directional pad functions more like a touchpad. Bytes 3 & 4 + * contain a position coordinate (x,y), with each component ranging + * from -14 to 14. We want to down-sample this to only 4 discrete values + * for up/down/left/right arrow keys. Also, when you get too close to + * diagonals, it has a tendancy to jump back and forth, so lets try to + * ignore when they get too close. + */ + if (ictx->product != 0xffdc) { + /* first, pad to 8 bytes so it conforms with everything else */ + buf[5] = buf[6] = buf[7] = 0; + timeout = 500; /* in msecs */ + /* (2*threshold) x (2*threshold) square */ + threshold = pad_thresh ? pad_thresh : 28; + rel_x = buf[2]; + rel_y = buf[3]; + + if (ictx->ir_type == IR_TYPE_OTHER && pad_stabilize) { + if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) { + dir = stabilize((int)rel_x, (int)rel_y, + timeout, threshold); + if (!dir) { + ictx->kc = KEY_UNKNOWN; + return; + } + buf[2] = dir & 0xFF; + buf[3] = (dir >> 8) & 0xFF; + memcpy(&temp_key, buf, sizeof(temp_key)); + remote_key = (u32) (le64_to_cpu(temp_key) + & 0xffffffff); + ictx->kc = imon_remote_key_lookup(ictx, + remote_key); + } + } else { + if (abs(rel_y) > abs(rel_x)) { + buf[2] = (rel_y > 0) ? 0x7F : 0x80; + buf[3] = 0; + ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP; + } else { + buf[2] = 0; + buf[3] = (rel_x > 0) ? 0x7F : 0x80; + ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT; + } + } + + /* + * Handle on-board decoded pad events for e.g. older VFD/iMON-Pad + * device (15c2:ffdc). The remote generates various codes from + * 0x68nnnnB7 to 0x6AnnnnB7, the left mouse button generates + * 0x688301b7 and the right one 0x688481b7. All other keys generate + * 0x2nnnnnnn. Position coordinate is encoded in buf[1] and buf[2] with + * reversed endianess. Extract direction from buffer, rotate endianess, + * adjust sign and feed the values into stabilize(). The resulting codes + * will be 0x01008000, 0x01007F00, which match the newer devices. + */ + } else { + timeout = 10; /* in msecs */ + /* (2*threshold) x (2*threshold) square */ + threshold = pad_thresh ? pad_thresh : 15; + + /* buf[1] is x */ + rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 | + (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6; + if (buf[0] & 0x02) + rel_x |= ~0x10+1; + /* buf[2] is y */ + rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 | + (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6; + if (buf[0] & 0x01) + rel_y |= ~0x10+1; + + buf[0] = 0x01; + buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0; + + if (ictx->ir_type == IR_TYPE_OTHER && pad_stabilize) { + dir = stabilize((int)rel_x, (int)rel_y, + timeout, threshold); + if (!dir) { + ictx->kc = KEY_UNKNOWN; + return; + } + buf[2] = dir & 0xFF; + buf[3] = (dir >> 8) & 0xFF; + memcpy(&temp_key, buf, sizeof(temp_key)); + remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff); + ictx->kc = imon_remote_key_lookup(ictx, remote_key); + } else { + if (abs(rel_y) > abs(rel_x)) { + buf[2] = (rel_y > 0) ? 0x7F : 0x80; + buf[3] = 0; + ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP; + } else { + buf[2] = 0; + buf[3] = (rel_x > 0) ? 0x7F : 0x80; + ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT; + } + } + } +} + +static int imon_parse_press_type(struct imon_context *ictx, + unsigned char *buf, u8 ktype) +{ + int press_type = 0; + int rep_delay = ictx->idev->rep[REP_DELAY]; + int rep_period = ictx->idev->rep[REP_PERIOD]; + + /* key release of 0x02XXXXXX key */ + if (ictx->kc == KEY_RESERVED && buf[0] == 0x02 && buf[3] == 0x00) + ictx->kc = ictx->last_keycode; + + /* mouse button release on (some) 0xffdc devices */ + else if (ictx->kc == KEY_RESERVED && buf[0] == 0x68 && buf[1] == 0x82 && + buf[2] == 0x81 && buf[3] == 0xb7) + ictx->kc = ictx->last_keycode; + + /* mouse button release on (some other) 0xffdc devices */ + else if (ictx->kc == KEY_RESERVED && buf[0] == 0x01 && buf[1] == 0x00 && + buf[2] == 0x81 && buf[3] == 0xb7) + ictx->kc = ictx->last_keycode; + + /* mce-specific button handling */ + else if (ktype == IMON_KEY_MCE) { + /* initial press */ + if (ictx->kc != ictx->last_keycode + || buf[2] != ictx->mce_toggle_bit) { + ictx->last_keycode = ictx->kc; + ictx->mce_toggle_bit = buf[2]; + press_type = 1; + mod_timer(&ictx->itimer, + jiffies + msecs_to_jiffies(rep_delay)); + /* repeat */ + } else { + press_type = 2; + mod_timer(&ictx->itimer, + jiffies + msecs_to_jiffies(rep_period)); + } + + /* incoherent or irrelevant data */ + } else if (ictx->kc == KEY_RESERVED) + press_type = -EINVAL; + + /* key release of 0xXXXXXXb7 key */ + else if (ictx->release_code) + press_type = 0; + + /* this is a button press */ + else + press_type = 1; + + return press_type; +} + +/** + * Process the incoming packet + */ +static void imon_incoming_packet(struct imon_context *ictx, + struct urb *urb, int intf) +{ + int len = urb->actual_length; + unsigned char *buf = urb->transfer_buffer; + struct device *dev = ictx->dev; + u32 kc; + bool norelease = 0; + int i; + u64 temp_key; + u64 panel_key = 0; + u32 remote_key = 0; + struct input_dev *idev = NULL; + int press_type = 0; + int msec; + struct timeval t; + static struct timeval prev_time = { 0, 0 }; + u8 ktype = IMON_KEY_IMON; + + idev = ictx->idev; + + /* filter out junk data on the older 0xffdc imon devices */ + if ((buf[0] == 0xff) && (buf[7] == 0xff)) + return; + + /* Figure out what key was pressed */ + memcpy(&temp_key, buf, sizeof(temp_key)); + if (len == 8 && buf[7] == 0xee) { + ktype = IMON_KEY_PANEL; + panel_key = le64_to_cpu(temp_key); + kc = imon_panel_key_lookup(panel_key); + } else { + remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff); + if (ictx->ir_type == IR_TYPE_RC6) { + if (buf[0] == 0x80) + ktype = IMON_KEY_MCE; + kc = imon_mce_key_lookup(ictx, remote_key); + } else + kc = imon_remote_key_lookup(ictx, remote_key); + } + + /* keyboard/mouse mode toggle button */ + if (kc == KEY_KEYBOARD && !ictx->release_code) { + ictx->last_keycode = kc; + if (!nomouse) { + ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1; + dev_dbg(dev, "toggling to %s mode\n", + ictx->pad_mouse ? "mouse" : "keyboard"); + return; + } else { + ictx->pad_mouse = 0; + dev_dbg(dev, "mouse mode disabled, passing key value\n"); + } + } + + ictx->kc = kc; + + /* send touchscreen events through input subsystem if touchpad data */ + if (ictx->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 && + buf[7] == 0x86) { + imon_touch_event(ictx, buf); + + /* look for mouse events with pad in mouse mode */ + } else if (ictx->pad_mouse) { + if (imon_mouse_event(ictx, buf, len)) + return; + } + + /* Now for some special handling to convert pad input to arrow keys */ + if (((len == 5) && (buf[0] == 0x01) && (buf[4] == 0x00)) || + ((len == 8) && (buf[0] & 0x40) && + !(buf[1] & 0x1 || buf[1] >> 2 & 0x1))) { + len = 8; + imon_pad_to_keys(ictx, buf); + norelease = 1; + } + + if (debug) { + printk(KERN_INFO "intf%d decoded packet: ", intf); + for (i = 0; i < len; ++i) + printk("%02x ", buf[i]); + printk("\n"); + } + + press_type = imon_parse_press_type(ictx, buf, ktype); + if (press_type < 0) + goto not_input_data; + + if (ictx->kc == KEY_UNKNOWN) + goto unknown_key; + + /* KEY_MUTE repeats from MCE and knob need to be suppressed */ + if ((ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) + && (buf[7] == 0xee || ktype == IMON_KEY_MCE)) { + do_gettimeofday(&t); + msec = tv2int(&t, &prev_time); + prev_time = t; + if (msec < idev->rep[REP_DELAY]) + return; + } + + input_report_key(idev, ictx->kc, press_type); + input_sync(idev); + + /* panel keys and some remote keys don't generate a release */ + if (panel_key || norelease) { + input_report_key(idev, ictx->kc, 0); + input_sync(idev); + } + + ictx->last_keycode = ictx->kc; + + return; + +unknown_key: + dev_info(dev, "%s: unknown keypress, code 0x%llx\n", __func__, + (panel_key ? be64_to_cpu(panel_key) : + be32_to_cpu(remote_key))); + return; + +not_input_data: + if (len != 8) { + dev_warn(dev, "imon %s: invalid incoming packet " + "size (len = %d, intf%d)\n", __func__, len, intf); + return; + } + + /* iMON 2.4G associate frame */ + if (buf[0] == 0x00 && + buf[2] == 0xFF && /* REFID */ + buf[3] == 0xFF && + buf[4] == 0xFF && + buf[5] == 0xFF && /* iMON 2.4G */ + ((buf[6] == 0x4E && buf[7] == 0xDF) || /* LT */ + (buf[6] == 0x5E && buf[7] == 0xDF))) { /* DT */ + dev_warn(dev, "%s: remote associated refid=%02X\n", + __func__, buf[1]); + ictx->rf_isassociating = 0; + } +} + +/** + * Callback function for USB core API: receive data + */ +static void usb_rx_callback_intf0(struct urb *urb) +{ + struct imon_context *ictx; + int intfnum = 0; + + if (!urb) + return; + + ictx = (struct imon_context *)urb->context; + if (!ictx) + return; + + switch (urb->status) { + case -ENOENT: /* usbcore unlink successful! */ + return; + + case -ESHUTDOWN: /* transport endpoint was shut down */ + break; + + case 0: + imon_incoming_packet(ictx, urb, intfnum); + break; + + default: + dev_warn(ictx->dev, "imon %s: status(%d): ignored\n", + __func__, urb->status); + break; + } + + usb_submit_urb(ictx->rx_urb_intf0, GFP_ATOMIC); +} + +static void usb_rx_callback_intf1(struct urb *urb) +{ + struct imon_context *ictx; + int intfnum = 1; + + if (!urb) + return; + + ictx = (struct imon_context *)urb->context; + if (!ictx) + return; + + switch (urb->status) { + case -ENOENT: /* usbcore unlink successful! */ + return; + + case -ESHUTDOWN: /* transport endpoint was shut down */ + break; + + case 0: + imon_incoming_packet(ictx, urb, intfnum); + break; + + default: + dev_warn(ictx->dev, "imon %s: status(%d): ignored\n", + __func__, urb->status); + break; + } + + usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC); +} + +static struct input_dev *imon_init_idev(struct imon_context *ictx) +{ + struct input_dev *idev; + struct ir_dev_props *props; + struct ir_input_dev *ir; + int ret, i; + + idev = input_allocate_device(); + if (!idev) { + dev_err(ictx->dev, "remote input dev allocation failed\n"); + goto idev_alloc_failed; + } + + props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL); + if (!props) { + dev_err(ictx->dev, "remote ir dev props allocation failed\n"); + goto props_alloc_failed; + } + + ir = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL); + if (!ir) { + dev_err(ictx->dev, "remote ir input dev allocation failed\n"); + goto ir_dev_alloc_failed; + } + + snprintf(ictx->name_idev, sizeof(ictx->name_idev), + "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product); + idev->name = ictx->name_idev; + + usb_make_path(ictx->usbdev_intf0, ictx->phys_idev, + sizeof(ictx->phys_idev)); + strlcat(ictx->phys_idev, "/input0", sizeof(ictx->phys_idev)); + idev->phys = ictx->phys_idev; + + idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL); + + idev->keybit[BIT_WORD(BTN_MOUSE)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); + idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) | + BIT_MASK(REL_WHEEL); + + /* panel and/or knob code support */ + for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) { + u32 kc = imon_panel_key_table[i].keycode; + __set_bit(kc, idev->keybit); + } + + props->priv = ictx; + props->driver_type = RC_DRIVER_SCANCODE; + /* IR_TYPE_OTHER maps to iMON PAD remote, IR_TYPE_RC6 to MCE remote */ + props->allowed_protos = IR_TYPE_OTHER | IR_TYPE_RC6; + props->change_protocol = imon_ir_change_protocol; + ictx->props = props; + + ictx->ir = ir; + memcpy(&ir->dev, ictx->dev, sizeof(struct device)); + + usb_to_input_id(ictx->usbdev_intf0, &idev->id); + idev->dev.parent = ictx->dev; + + input_set_drvdata(idev, ir); + + ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME); + if (ret < 0) { + dev_err(ictx->dev, "remote input dev register failed\n"); + goto idev_register_failed; + } + + return idev; + +idev_register_failed: + kfree(ir); +ir_dev_alloc_failed: + kfree(props); +props_alloc_failed: + input_free_device(idev); +idev_alloc_failed: + + return NULL; +} + +static struct input_dev *imon_init_touch(struct imon_context *ictx) +{ + struct input_dev *touch; + int ret; + + touch = input_allocate_device(); + if (!touch) { + dev_err(ictx->dev, "touchscreen input dev allocation failed\n"); + goto touch_alloc_failed; + } + + snprintf(ictx->name_touch, sizeof(ictx->name_touch), + "iMON USB Touchscreen (%04x:%04x)", + ictx->vendor, ictx->product); + touch->name = ictx->name_touch; + + usb_make_path(ictx->usbdev_intf1, ictx->phys_touch, + sizeof(ictx->phys_touch)); + strlcat(ictx->phys_touch, "/input1", sizeof(ictx->phys_touch)); + touch->phys = ictx->phys_touch; + + touch->evbit[0] = + BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + touch->keybit[BIT_WORD(BTN_TOUCH)] = + BIT_MASK(BTN_TOUCH); + input_set_abs_params(touch, ABS_X, + 0x00, 0xfff, 0, 0); + input_set_abs_params(touch, ABS_Y, + 0x00, 0xfff, 0, 0); + + input_set_drvdata(touch, ictx); + + usb_to_input_id(ictx->usbdev_intf1, &touch->id); + touch->dev.parent = ictx->dev; + ret = input_register_device(touch); + if (ret < 0) { + dev_info(ictx->dev, "touchscreen input dev register failed\n"); + goto touch_register_failed; + } + + return touch; + +touch_register_failed: + input_free_device(ictx->touch); + +touch_alloc_failed: + return NULL; +} + +static bool imon_find_endpoints(struct imon_context *ictx, + struct usb_host_interface *iface_desc) +{ + struct usb_endpoint_descriptor *ep; + struct usb_endpoint_descriptor *rx_endpoint = NULL; + struct usb_endpoint_descriptor *tx_endpoint = NULL; + int ifnum = iface_desc->desc.bInterfaceNumber; + int num_endpts = iface_desc->desc.bNumEndpoints; + int i, ep_dir, ep_type; + bool ir_ep_found = 0; + bool display_ep_found = 0; + bool tx_control = 0; + + /* + * Scan the endpoint list and set: + * first input endpoint = IR endpoint + * first output endpoint = display endpoint + */ + for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) { + ep = &iface_desc->endpoint[i].desc; + ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; + ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + if (!ir_ep_found && ep_dir == USB_DIR_IN && + ep_type == USB_ENDPOINT_XFER_INT) { + + rx_endpoint = ep; + ir_ep_found = 1; + dev_dbg(ictx->dev, "%s: found IR endpoint\n", __func__); + + } else if (!display_ep_found && ep_dir == USB_DIR_OUT && + ep_type == USB_ENDPOINT_XFER_INT) { + tx_endpoint = ep; + display_ep_found = 1; + dev_dbg(ictx->dev, "%s: found display endpoint\n", __func__); + } + } + + if (ifnum == 0) { + ictx->rx_endpoint_intf0 = rx_endpoint; + /* + * tx is used to send characters to lcd/vfd, associate RF + * remotes, set IR protocol, and maybe more... + */ + ictx->tx_endpoint = tx_endpoint; + } else { + ictx->rx_endpoint_intf1 = rx_endpoint; + } + + /* + * If we didn't find a display endpoint, this is probably one of the + * newer iMON devices that use control urb instead of interrupt + */ + if (!display_ep_found) { + tx_control = 1; + display_ep_found = 1; + dev_dbg(ictx->dev, "%s: device uses control endpoint, not " + "interface OUT endpoint\n", __func__); + } + + /* + * Some iMON receivers have no display. Unfortunately, it seems + * that SoundGraph recycles device IDs between devices both with + * and without... :\ + */ + if (ictx->display_type == IMON_DISPLAY_TYPE_NONE) { + display_ep_found = 0; + dev_dbg(ictx->dev, "%s: device has no display\n", __func__); + } + + /* + * iMON Touch devices have a VGA touchscreen, but no "display", as + * that refers to e.g. /dev/lcd0 (a character device LCD or VFD). + */ + if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) { + display_ep_found = 0; + dev_dbg(ictx->dev, "%s: iMON Touch device found\n", __func__); + } + + /* Input endpoint is mandatory */ + if (!ir_ep_found) + err("%s: no valid input (IR) endpoint found.", __func__); + + ictx->tx_control = tx_control; + + if (display_ep_found) + ictx->display_supported = true; + + return ir_ep_found; + +} + +static struct imon_context *imon_init_intf0(struct usb_interface *intf) +{ + struct imon_context *ictx; + struct urb *rx_urb; + struct urb *tx_urb; + struct device *dev = &intf->dev; + struct usb_host_interface *iface_desc; + int ret = -ENOMEM; + + ictx = kzalloc(sizeof(struct imon_context), GFP_KERNEL); + if (!ictx) { + dev_err(dev, "%s: kzalloc failed for context", __func__); + goto exit; + } + rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rx_urb) { + dev_err(dev, "%s: usb_alloc_urb failed for IR urb", __func__); + goto rx_urb_alloc_failed; + } + tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tx_urb) { + dev_err(dev, "%s: usb_alloc_urb failed for display urb", + __func__); + goto tx_urb_alloc_failed; + } + + mutex_init(&ictx->lock); + + mutex_lock(&ictx->lock); + + ictx->dev = dev; + ictx->usbdev_intf0 = usb_get_dev(interface_to_usbdev(intf)); + ictx->dev_present_intf0 = 1; + ictx->rx_urb_intf0 = rx_urb; + ictx->tx_urb = tx_urb; + + ictx->vendor = le16_to_cpu(ictx->usbdev_intf0->descriptor.idVendor); + ictx->product = le16_to_cpu(ictx->usbdev_intf0->descriptor.idProduct); + + ret = -ENODEV; + iface_desc = intf->cur_altsetting; + if (!imon_find_endpoints(ictx, iface_desc)) { + goto find_endpoint_failed; + } + + ictx->idev = imon_init_idev(ictx); + if (!ictx->idev) { + dev_err(dev, "%s: input device setup failed\n", __func__); + goto idev_setup_failed; + } + + usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0, + usb_rcvintpipe(ictx->usbdev_intf0, + ictx->rx_endpoint_intf0->bEndpointAddress), + ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf), + usb_rx_callback_intf0, ictx, + ictx->rx_endpoint_intf0->bInterval); + + ret = usb_submit_urb(ictx->rx_urb_intf0, GFP_KERNEL); + if (ret) { + err("%s: usb_submit_urb failed for intf0 (%d)", + __func__, ret); + goto urb_submit_failed; + } + + return ictx; + +urb_submit_failed: + input_unregister_device(ictx->idev); + input_free_device(ictx->idev); +idev_setup_failed: +find_endpoint_failed: + mutex_unlock(&ictx->lock); + usb_free_urb(tx_urb); +tx_urb_alloc_failed: + usb_free_urb(rx_urb); +rx_urb_alloc_failed: + kfree(ictx); +exit: + dev_err(dev, "unable to initialize intf0, err %d\n", ret); + + return NULL; +} + +static struct imon_context *imon_init_intf1(struct usb_interface *intf, + struct imon_context *ictx) +{ + struct urb *rx_urb; + struct usb_host_interface *iface_desc; + int ret = -ENOMEM; + + rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rx_urb) { + err("%s: usb_alloc_urb failed for IR urb", __func__); + goto rx_urb_alloc_failed; + } + + mutex_lock(&ictx->lock); + + if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) { + init_timer(&ictx->ttimer); + ictx->ttimer.data = (unsigned long)ictx; + ictx->ttimer.function = imon_touch_display_timeout; + } + + ictx->usbdev_intf1 = usb_get_dev(interface_to_usbdev(intf)); + ictx->dev_present_intf1 = 1; + ictx->rx_urb_intf1 = rx_urb; + + ret = -ENODEV; + iface_desc = intf->cur_altsetting; + if (!imon_find_endpoints(ictx, iface_desc)) + goto find_endpoint_failed; + + if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) { + ictx->touch = imon_init_touch(ictx); + if (!ictx->touch) + goto touch_setup_failed; + } else + ictx->touch = NULL; + + usb_fill_int_urb(ictx->rx_urb_intf1, ictx->usbdev_intf1, + usb_rcvintpipe(ictx->usbdev_intf1, + ictx->rx_endpoint_intf1->bEndpointAddress), + ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf), + usb_rx_callback_intf1, ictx, + ictx->rx_endpoint_intf1->bInterval); + + ret = usb_submit_urb(ictx->rx_urb_intf1, GFP_KERNEL); + + if (ret) { + err("%s: usb_submit_urb failed for intf1 (%d)", + __func__, ret); + goto urb_submit_failed; + } + + return ictx; + +urb_submit_failed: + if (ictx->touch) { + input_unregister_device(ictx->touch); + input_free_device(ictx->touch); + } +touch_setup_failed: +find_endpoint_failed: + mutex_unlock(&ictx->lock); + usb_free_urb(rx_urb); +rx_urb_alloc_failed: + dev_err(ictx->dev, "unable to initialize intf0, err %d\n", ret); + + return NULL; +} + +/* + * The 0x15c2:0xffdc device ID was used for umpteen different imon + * devices, and all of them constantly spew interrupts, even when there + * is no actual data to report. However, byte 6 of this buffer looks like + * its unique across device variants, so we're trying to key off that to + * figure out which display type (if any) and what IR protocol the device + * actually supports. These devices have their IR protocol hard-coded into + * their firmware, they can't be changed on the fly like the newer hardware. + */ +static void imon_get_ffdc_type(struct imon_context *ictx) +{ + u8 ffdc_cfg_byte = ictx->usb_rx_buf[6]; + u8 detected_display_type = IMON_DISPLAY_TYPE_NONE; + u64 allowed_protos = IR_TYPE_OTHER; + + switch (ffdc_cfg_byte) { + /* iMON Knob, no display, iMON IR + vol knob */ + case 0x21: + dev_info(ictx->dev, "0xffdc iMON Knob, iMON IR"); + ictx->display_supported = false; + break; + /* iMON VFD, no IR (does have vol knob tho) */ + case 0x35: + dev_info(ictx->dev, "0xffdc iMON VFD + knob, no IR"); + detected_display_type = IMON_DISPLAY_TYPE_VFD; + break; + /* iMON VFD, iMON IR */ + case 0x24: + case 0x85: + dev_info(ictx->dev, "0xffdc iMON VFD, iMON IR"); + detected_display_type = IMON_DISPLAY_TYPE_VFD; + break; + /* iMON LCD, MCE IR */ + case 0x9f: + dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR"); + detected_display_type = IMON_DISPLAY_TYPE_LCD; + allowed_protos = IR_TYPE_RC6; + break; + default: + dev_info(ictx->dev, "Unknown 0xffdc device, " + "defaulting to VFD and iMON IR"); + detected_display_type = IMON_DISPLAY_TYPE_VFD; + break; + } + + printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte); + + ictx->display_type = detected_display_type; + ictx->props->allowed_protos = allowed_protos; + ictx->ir_type = allowed_protos; +} + +static void imon_set_display_type(struct imon_context *ictx, + struct usb_interface *intf) +{ + u8 configured_display_type = IMON_DISPLAY_TYPE_VFD; + + /* + * Try to auto-detect the type of display if the user hasn't set + * it by hand via the display_type modparam. Default is VFD. + */ + + if (display_type == IMON_DISPLAY_TYPE_AUTO) { + switch (ictx->product) { + case 0xffdc: + /* set in imon_get_ffdc_type() */ + configured_display_type = ictx->display_type; + break; + case 0x0034: + case 0x0035: + configured_display_type = IMON_DISPLAY_TYPE_VGA; + break; + case 0x0038: + case 0x0039: + case 0x0045: + configured_display_type = IMON_DISPLAY_TYPE_LCD; + break; + case 0x003c: + case 0x0041: + case 0x0042: + case 0x0043: + configured_display_type = IMON_DISPLAY_TYPE_NONE; + ictx->display_supported = false; + break; + case 0x0036: + case 0x0044: + default: + configured_display_type = IMON_DISPLAY_TYPE_VFD; + break; + } + } else { + configured_display_type = display_type; + if (display_type == IMON_DISPLAY_TYPE_NONE) + ictx->display_supported = false; + else + ictx->display_supported = true; + dev_info(ictx->dev, "%s: overriding display type to %d via " + "modparam\n", __func__, display_type); + } + + ictx->display_type = configured_display_type; +} + +static void imon_init_display(struct imon_context *ictx, + struct usb_interface *intf) +{ + int ret; + + dev_dbg(ictx->dev, "Registering iMON display with sysfs\n"); + + /* set up sysfs entry for built-in clock */ + ret = sysfs_create_group(&intf->dev.kobj, + &imon_display_attribute_group); + if (ret) + dev_err(ictx->dev, "Could not create display sysfs " + "entries(%d)", ret); + + if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) + ret = usb_register_dev(intf, &imon_lcd_class); + else + ret = usb_register_dev(intf, &imon_vfd_class); + if (ret) + /* Not a fatal error, so ignore */ + dev_info(ictx->dev, "could not get a minor number for " + "display\n"); + +} + +/** + * Callback function for USB core API: Probe + */ +static int __devinit imon_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev = NULL; + struct usb_host_interface *iface_desc = NULL; + struct usb_interface *first_if; + struct device *dev = &interface->dev; + int ifnum, code_length, sysfs_err; + int ret = 0; + struct imon_context *ictx = NULL; + struct imon_context *first_if_ctx = NULL; + u16 vendor, product; + const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x88 }; + + code_length = BUF_CHUNK_SIZE * 8; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + iface_desc = interface->cur_altsetting; + ifnum = iface_desc->desc.bInterfaceNumber; + vendor = le16_to_cpu(usbdev->descriptor.idVendor); + product = le16_to_cpu(usbdev->descriptor.idProduct); + + dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n", + __func__, vendor, product, ifnum); + + /* prevent races probing devices w/multiple interfaces */ + mutex_lock(&driver_lock); + + first_if = usb_ifnum_to_if(usbdev, 0); + first_if_ctx = (struct imon_context *)usb_get_intfdata(first_if); + + if (ifnum == 0) { + ictx = imon_init_intf0(interface); + if (!ictx) { + err("%s: failed to initialize context!\n", __func__); + ret = -ENODEV; + goto fail; + } + + if (product == 0xffdc) { + /* RF products *also* use 0xffdc... sigh... */ + sysfs_err = sysfs_create_group(&interface->dev.kobj, + &imon_rf_attribute_group); + if (sysfs_err) + err("%s: Could not create RF sysfs entries(%d)", + __func__, sysfs_err); + } + + } else { + /* this is the secondary interface on the device */ + ictx = imon_init_intf1(interface, first_if_ctx); + if (!ictx) { + err("%s: failed to attach to context!\n", __func__); + ret = -ENODEV; + goto fail; + } + + } + + usb_set_intfdata(interface, ictx); + + if (ifnum == 0) { + /* Enable front-panel buttons and/or knobs */ + memcpy(ictx->usb_tx_buf, &fp_packet, sizeof(fp_packet)); + ret = send_packet(ictx); + /* Not fatal, but warn about it */ + if (ret) + dev_info(dev, "failed to enable panel buttons " + "and/or knobs\n"); + + if (product == 0xffdc) + imon_get_ffdc_type(ictx); + + imon_set_display_type(ictx, interface); + + if (ictx->display_supported) + imon_init_display(ictx, interface); + } + + /* set IR protocol/remote type */ + ret = imon_ir_change_protocol(ictx, ictx->ir_type); + if (ret) { + dev_warn(dev, "%s: failed to set IR protocol, falling back " + "to standard iMON protocol mode\n", __func__); + ictx->ir_type = IR_TYPE_OTHER; + } + + dev_info(dev, "iMON device (%04x:%04x, intf%d) on " + "usb<%d:%d> initialized\n", vendor, product, ifnum, + usbdev->bus->busnum, usbdev->devnum); + + mutex_unlock(&ictx->lock); + mutex_unlock(&driver_lock); + + return 0; + +fail: + mutex_unlock(&driver_lock); + dev_err(dev, "unable to register, err %d\n", ret); + + return ret; +} + +/** + * Callback function for USB core API: disconnect + */ +static void __devexit imon_disconnect(struct usb_interface *interface) +{ + struct imon_context *ictx; + struct device *dev; + int ifnum; + + /* prevent races with multi-interface device probing and display_open */ + mutex_lock(&driver_lock); + + ictx = usb_get_intfdata(interface); + dev = ictx->dev; + ifnum = interface->cur_altsetting->desc.bInterfaceNumber; + + mutex_lock(&ictx->lock); + + /* + * sysfs_remove_group is safe to call even if sysfs_create_group + * hasn't been called + */ + sysfs_remove_group(&interface->dev.kobj, + &imon_display_attribute_group); + sysfs_remove_group(&interface->dev.kobj, + &imon_rf_attribute_group); + + usb_set_intfdata(interface, NULL); + + /* Abort ongoing write */ + if (ictx->tx.busy) { + usb_kill_urb(ictx->tx_urb); + complete_all(&ictx->tx.finished); + } + + if (ifnum == 0) { + ictx->dev_present_intf0 = 0; + usb_kill_urb(ictx->rx_urb_intf0); + input_unregister_device(ictx->idev); + if (ictx->display_supported) { + if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) + usb_deregister_dev(interface, &imon_lcd_class); + else + usb_deregister_dev(interface, &imon_vfd_class); + } + } else { + ictx->dev_present_intf1 = 0; + usb_kill_urb(ictx->rx_urb_intf1); + if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) + input_unregister_device(ictx->touch); + } + + if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1) { + if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) + del_timer_sync(&ictx->ttimer); + mutex_unlock(&ictx->lock); + if (!ictx->display_isopen) + free_imon_context(ictx); + } else { + if (ictx->ir_type == IR_TYPE_RC6) + del_timer_sync(&ictx->itimer); + mutex_unlock(&ictx->lock); + } + + mutex_unlock(&driver_lock); + + dev_dbg(dev, "%s: iMON device (intf%d) disconnected\n", + __func__, ifnum); +} + +static int imon_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct imon_context *ictx = usb_get_intfdata(intf); + int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; + + if (ifnum == 0) + usb_kill_urb(ictx->rx_urb_intf0); + else + usb_kill_urb(ictx->rx_urb_intf1); + + return 0; +} + +static int imon_resume(struct usb_interface *intf) +{ + int rc = 0; + struct imon_context *ictx = usb_get_intfdata(intf); + int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; + + if (ifnum == 0) { + usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0, + usb_rcvintpipe(ictx->usbdev_intf0, + ictx->rx_endpoint_intf0->bEndpointAddress), + ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf), + usb_rx_callback_intf0, ictx, + ictx->rx_endpoint_intf0->bInterval); + + rc = usb_submit_urb(ictx->rx_urb_intf0, GFP_ATOMIC); + + } else { + usb_fill_int_urb(ictx->rx_urb_intf1, ictx->usbdev_intf1, + usb_rcvintpipe(ictx->usbdev_intf1, + ictx->rx_endpoint_intf1->bEndpointAddress), + ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf), + usb_rx_callback_intf1, ictx, + ictx->rx_endpoint_intf1->bInterval); + + rc = usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC); + } + + return rc; +} + +static int __init imon_init(void) +{ + int rc; + + rc = usb_register(&imon_driver); + if (rc) { + err("%s: usb register failed(%d)", __func__, rc); + rc = -ENODEV; + } + + return rc; +} + +static void __exit imon_exit(void) +{ + usb_deregister(&imon_driver); +} + +module_init(imon_init); +module_exit(imon_exit); diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h new file mode 100644 index 000000000000..9a5e65a471a5 --- /dev/null +++ b/drivers/media/IR/ir-core-priv.h @@ -0,0 +1,126 @@ +/* + * Remote Controller core raw events header + * + * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + * + * 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 _IR_RAW_EVENT +#define _IR_RAW_EVENT + +#include <linux/slab.h> +#include <media/ir-core.h> + +struct ir_raw_handler { + struct list_head list; + + int (*decode)(struct input_dev *input_dev, struct ir_raw_event event); + int (*raw_register)(struct input_dev *input_dev); + int (*raw_unregister)(struct input_dev *input_dev); +}; + +struct ir_raw_event_ctrl { + struct work_struct rx_work; /* for the rx decoding workqueue */ + struct kfifo kfifo; /* fifo for the pulse/space durations */ + ktime_t last_event; /* when last event occurred */ + enum raw_event_type last_type; /* last event type */ + struct input_dev *input_dev; /* pointer to the parent input_dev */ +}; + +/* macros for IR decoders */ +static inline bool geq_margin(unsigned d1, unsigned d2, unsigned margin) +{ + return d1 > (d2 - margin); +} + +static inline bool eq_margin(unsigned d1, unsigned d2, unsigned margin) +{ + return ((d1 > (d2 - margin)) && (d1 < (d2 + margin))); +} + +static inline bool is_transition(struct ir_raw_event *x, struct ir_raw_event *y) +{ + return x->pulse != y->pulse; +} + +static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration) +{ + if (duration > ev->duration) + ev->duration = 0; + else + ev->duration -= duration; +} + +#define TO_US(duration) (((duration) + 500) / 1000) +#define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space") +#define IS_RESET(ev) (ev.duration == 0) + +/* + * Routines from ir-sysfs.c - Meant to be called only internally inside + * ir-core + */ + +int ir_register_class(struct input_dev *input_dev); +void ir_unregister_class(struct input_dev *input_dev); + +/* + * Routines from ir-raw-event.c to be used internally and by decoders + */ +int ir_raw_event_register(struct input_dev *input_dev); +void ir_raw_event_unregister(struct input_dev *input_dev); +int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler); +void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler); +void ir_raw_init(void); + + +/* + * Decoder initialization code + * + * Those load logic are called during ir-core init, and automatically + * loads the compiled decoders for their usage with IR raw events + */ + +/* from ir-nec-decoder.c */ +#ifdef CONFIG_IR_NEC_DECODER_MODULE +#define load_nec_decode() request_module("ir-nec-decoder") +#else +#define load_nec_decode() 0 +#endif + +/* from ir-rc5-decoder.c */ +#ifdef CONFIG_IR_RC5_DECODER_MODULE +#define load_rc5_decode() request_module("ir-rc5-decoder") +#else +#define load_rc5_decode() 0 +#endif + +/* from ir-rc6-decoder.c */ +#ifdef CONFIG_IR_RC6_DECODER_MODULE +#define load_rc6_decode() request_module("ir-rc6-decoder") +#else +#define load_rc6_decode() 0 +#endif + +/* from ir-jvc-decoder.c */ +#ifdef CONFIG_IR_JVC_DECODER_MODULE +#define load_jvc_decode() request_module("ir-jvc-decoder") +#else +#define load_jvc_decode() 0 +#endif + +/* from ir-sony-decoder.c */ +#ifdef CONFIG_IR_SONY_DECODER_MODULE +#define load_sony_decode() request_module("ir-sony-decoder") +#else +#define load_sony_decode() 0 +#endif + +#endif /* _IR_RAW_EVENT */ diff --git a/drivers/media/IR/ir-functions.c b/drivers/media/IR/ir-functions.c index ab06919ad5fc..db591e421887 100644 --- a/drivers/media/IR/ir-functions.c +++ b/drivers/media/IR/ir-functions.c @@ -24,6 +24,7 @@ #include <linux/string.h> #include <linux/jiffies.h> #include <media/ir-common.h> +#include "ir-core-priv.h" /* -------------------------------------------------------------------------- */ diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c new file mode 100644 index 000000000000..0b804944cbb0 --- /dev/null +++ b/drivers/media/IR/ir-jvc-decoder.c @@ -0,0 +1,320 @@ +/* ir-jvc-decoder.c - handle JVC IR Pulse/Space protocol + * + * Copyright (C) 2010 by David Härdeman <david@hardeman.nu> + * + * 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. + * + * 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 <linux/bitrev.h> +#include "ir-core-priv.h" + +#define JVC_NBITS 16 /* dev(8) + func(8) */ +#define JVC_UNIT 525000 /* ns */ +#define JVC_HEADER_PULSE (16 * JVC_UNIT) /* lack of header -> repeat */ +#define JVC_HEADER_SPACE (8 * JVC_UNIT) +#define JVC_BIT_PULSE (1 * JVC_UNIT) +#define JVC_BIT_0_SPACE (1 * JVC_UNIT) +#define JVC_BIT_1_SPACE (3 * JVC_UNIT) +#define JVC_TRAILER_PULSE (1 * JVC_UNIT) +#define JVC_TRAILER_SPACE (35 * JVC_UNIT) + +/* Used to register jvc_decoder clients */ +static LIST_HEAD(decoder_list); +DEFINE_SPINLOCK(decoder_lock); + +enum jvc_state { + STATE_INACTIVE, + STATE_HEADER_SPACE, + STATE_BIT_PULSE, + STATE_BIT_SPACE, + STATE_TRAILER_PULSE, + STATE_TRAILER_SPACE, +}; + +struct decoder_data { + struct list_head list; + struct ir_input_dev *ir_dev; + int enabled:1; + + /* State machine control */ + enum jvc_state state; + u16 jvc_bits; + u16 jvc_old_bits; + unsigned count; + bool first; + bool toggle; +}; + + +/** + * get_decoder_data() - gets decoder data + * @input_dev: input device + * + * Returns the struct decoder_data that corresponds to a device + */ +static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) +{ + struct decoder_data *data = NULL; + + spin_lock(&decoder_lock); + list_for_each_entry(data, &decoder_list, list) { + if (data->ir_dev == ir_dev) + break; + } + spin_unlock(&decoder_lock); + return data; +} + +static ssize_t store_enabled(struct device *d, + struct device_attribute *mattr, + const char *buf, + size_t len) +{ + unsigned long value; + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (strict_strtoul(buf, 10, &value) || value > 1) + return -EINVAL; + + data->enabled = value; + + return len; +} + +static ssize_t show_enabled(struct device *d, + struct device_attribute *mattr, char *buf) +{ + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (data->enabled) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); + +static struct attribute *decoder_attributes[] = { + &dev_attr_enabled.attr, + NULL +}; + +static struct attribute_group decoder_attribute_group = { + .name = "jvc_decoder", + .attrs = decoder_attributes, +}; + +/** + * ir_jvc_decode() - Decode one JVC pulse or space + * @input_dev: the struct input_dev descriptor of the device + * @duration: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev) +{ + struct decoder_data *data; + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + + data = get_decoder_data(ir_dev); + if (!data) + return -EINVAL; + + if (!data->enabled) + return 0; + + if (IS_RESET(ev)) { + data->state = STATE_INACTIVE; + return 0; + } + + if (!geq_margin(ev.duration, JVC_UNIT, JVC_UNIT / 2)) + goto out; + + IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2)) + break; + + data->count = 0; + data->first = true; + data->toggle = !data->toggle; + data->state = STATE_HEADER_SPACE; + return 0; + + case STATE_HEADER_SPACE: + if (ev.pulse) + break; + + if (!eq_margin(ev.duration, JVC_HEADER_SPACE, JVC_UNIT / 2)) + break; + + data->state = STATE_BIT_PULSE; + return 0; + + case STATE_BIT_PULSE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, JVC_BIT_PULSE, JVC_UNIT / 2)) + break; + + data->state = STATE_BIT_SPACE; + return 0; + + case STATE_BIT_SPACE: + if (ev.pulse) + break; + + data->jvc_bits <<= 1; + if (eq_margin(ev.duration, JVC_BIT_1_SPACE, JVC_UNIT / 2)) { + data->jvc_bits |= 1; + decrease_duration(&ev, JVC_BIT_1_SPACE); + } else if (eq_margin(ev.duration, JVC_BIT_0_SPACE, JVC_UNIT / 2)) + decrease_duration(&ev, JVC_BIT_0_SPACE); + else + break; + data->count++; + + if (data->count == JVC_NBITS) + data->state = STATE_TRAILER_PULSE; + else + data->state = STATE_BIT_PULSE; + return 0; + + case STATE_TRAILER_PULSE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, JVC_TRAILER_PULSE, JVC_UNIT / 2)) + break; + + data->state = STATE_TRAILER_SPACE; + return 0; + + case STATE_TRAILER_SPACE: + if (ev.pulse) + break; + + if (!geq_margin(ev.duration, JVC_TRAILER_SPACE, JVC_UNIT / 2)) + break; + + if (data->first) { + u32 scancode; + scancode = (bitrev8((data->jvc_bits >> 8) & 0xff) << 8) | + (bitrev8((data->jvc_bits >> 0) & 0xff) << 0); + IR_dprintk(1, "JVC scancode 0x%04x\n", scancode); + ir_keydown(input_dev, scancode, data->toggle); + data->first = false; + data->jvc_old_bits = data->jvc_bits; + } else if (data->jvc_bits == data->jvc_old_bits) { + IR_dprintk(1, "JVC repeat\n"); + ir_repeat(input_dev); + } else { + IR_dprintk(1, "JVC invalid repeat msg\n"); + break; + } + + data->count = 0; + data->state = STATE_BIT_PULSE; + return 0; + } + +out: + IR_dprintk(1, "JVC decode failed at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static int ir_jvc_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct decoder_data *data; + int rc; + + rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); + if (rc < 0) + return rc; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + return -ENOMEM; + } + + data->ir_dev = ir_dev; + data->enabled = 1; + + spin_lock(&decoder_lock); + list_add_tail(&data->list, &decoder_list); + spin_unlock(&decoder_lock); + + return 0; +} + +static int ir_jvc_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + static struct decoder_data *data; + + data = get_decoder_data(ir_dev); + if (!data) + return 0; + + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + + spin_lock(&decoder_lock); + list_del(&data->list); + spin_unlock(&decoder_lock); + + return 0; +} + +static struct ir_raw_handler jvc_handler = { + .decode = ir_jvc_decode, + .raw_register = ir_jvc_register, + .raw_unregister = ir_jvc_unregister, +}; + +static int __init ir_jvc_decode_init(void) +{ + ir_raw_handler_register(&jvc_handler); + + printk(KERN_INFO "IR JVC protocol handler initialized\n"); + return 0; +} + +static void __exit ir_jvc_decode_exit(void) +{ + ir_raw_handler_unregister(&jvc_handler); +} + +module_init(ir_jvc_decode_init); +module_exit(ir_jvc_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); +MODULE_DESCRIPTION("JVC IR protocol decoder"); diff --git a/drivers/media/IR/ir-keymaps.c b/drivers/media/IR/ir-keymaps.c deleted file mode 100644 index 0efdefe75f32..000000000000 --- a/drivers/media/IR/ir-keymaps.c +++ /dev/null @@ -1,3494 +0,0 @@ -/* - Keytables for supported remote controls, used on drivers/media - devices. - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - * NOTICE FOR DEVELOPERS: - * The IR mappings should be as close as possible to what's - * specified at: - * http://linuxtv.org/wiki/index.php/Remote_Controllers - */ -#include <linux/module.h> - -#include <linux/input.h> -#include <media/ir-common.h> - -/* empty keytable, can be used as placeholder for not-yet created keytables */ -static struct ir_scancode ir_codes_empty[] = { - { 0x2a, KEY_COFFEE }, -}; - -struct ir_scancode_table ir_codes_empty_table = { - .scan = ir_codes_empty, - .size = ARRAY_SIZE(ir_codes_empty), -}; -EXPORT_SYMBOL_GPL(ir_codes_empty_table); - -/* Michal Majchrowicz <mmajchrowicz@gmail.com> */ -static struct ir_scancode ir_codes_proteus_2309[] = { - /* numeric */ - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x5c, KEY_POWER }, /* power */ - { 0x20, KEY_ZOOM }, /* full screen */ - { 0x0f, KEY_BACKSPACE }, /* recall */ - { 0x1b, KEY_ENTER }, /* mute */ - { 0x41, KEY_RECORD }, /* record */ - { 0x43, KEY_STOP }, /* stop */ - { 0x16, KEY_S }, - { 0x1a, KEY_POWER2 }, /* off */ - { 0x2e, KEY_RED }, - { 0x1f, KEY_CHANNELDOWN }, /* channel - */ - { 0x1c, KEY_CHANNELUP }, /* channel + */ - { 0x10, KEY_VOLUMEDOWN }, /* volume - */ - { 0x1e, KEY_VOLUMEUP }, /* volume + */ - { 0x14, KEY_F1 }, -}; - -struct ir_scancode_table ir_codes_proteus_2309_table = { - .scan = ir_codes_proteus_2309, - .size = ARRAY_SIZE(ir_codes_proteus_2309), -}; -EXPORT_SYMBOL_GPL(ir_codes_proteus_2309_table); - -/* Matt Jesson <dvb@jesson.eclipse.co.uk */ -static struct ir_scancode ir_codes_avermedia_dvbt[] = { - { 0x28, KEY_0 }, /* '0' / 'enter' */ - { 0x22, KEY_1 }, /* '1' */ - { 0x12, KEY_2 }, /* '2' / 'up arrow' */ - { 0x32, KEY_3 }, /* '3' */ - { 0x24, KEY_4 }, /* '4' / 'left arrow' */ - { 0x14, KEY_5 }, /* '5' */ - { 0x34, KEY_6 }, /* '6' / 'right arrow' */ - { 0x26, KEY_7 }, /* '7' */ - { 0x16, KEY_8 }, /* '8' / 'down arrow' */ - { 0x36, KEY_9 }, /* '9' */ - - { 0x20, KEY_LIST }, /* 'source' */ - { 0x10, KEY_TEXT }, /* 'teletext' */ - { 0x00, KEY_POWER }, /* 'power' */ - { 0x04, KEY_AUDIO }, /* 'audio' */ - { 0x06, KEY_ZOOM }, /* 'full screen' */ - { 0x18, KEY_VIDEO }, /* 'display' */ - { 0x38, KEY_SEARCH }, /* 'loop' */ - { 0x08, KEY_INFO }, /* 'preview' */ - { 0x2a, KEY_REWIND }, /* 'backward <<' */ - { 0x1a, KEY_FASTFORWARD }, /* 'forward >>' */ - { 0x3a, KEY_RECORD }, /* 'capture' */ - { 0x0a, KEY_MUTE }, /* 'mute' */ - { 0x2c, KEY_RECORD }, /* 'record' */ - { 0x1c, KEY_PAUSE }, /* 'pause' */ - { 0x3c, KEY_STOP }, /* 'stop' */ - { 0x0c, KEY_PLAY }, /* 'play' */ - { 0x2e, KEY_RED }, /* 'red' */ - { 0x01, KEY_BLUE }, /* 'blue' / 'cancel' */ - { 0x0e, KEY_YELLOW }, /* 'yellow' / 'ok' */ - { 0x21, KEY_GREEN }, /* 'green' */ - { 0x11, KEY_CHANNELDOWN }, /* 'channel -' */ - { 0x31, KEY_CHANNELUP }, /* 'channel +' */ - { 0x1e, KEY_VOLUMEDOWN }, /* 'volume -' */ - { 0x3e, KEY_VOLUMEUP }, /* 'volume +' */ -}; - -struct ir_scancode_table ir_codes_avermedia_dvbt_table = { - .scan = ir_codes_avermedia_dvbt, - .size = ARRAY_SIZE(ir_codes_avermedia_dvbt), -}; -EXPORT_SYMBOL_GPL(ir_codes_avermedia_dvbt_table); - -/* Mauro Carvalho Chehab <mchehab@infradead.org> */ -static struct ir_scancode ir_codes_avermedia_m135a[] = { - { 0x00, KEY_POWER2 }, - { 0x2e, KEY_DOT }, /* '.' */ - { 0x01, KEY_MODE }, /* TV/FM */ - - { 0x05, KEY_1 }, - { 0x06, KEY_2 }, - { 0x07, KEY_3 }, - { 0x09, KEY_4 }, - { 0x0a, KEY_5 }, - { 0x0b, KEY_6 }, - { 0x0d, KEY_7 }, - { 0x0e, KEY_8 }, - { 0x0f, KEY_9 }, - { 0x11, KEY_0 }, - - { 0x13, KEY_RIGHT }, /* -> */ - { 0x12, KEY_LEFT }, /* <- */ - - { 0x17, KEY_SLEEP }, /* Capturar Imagem */ - { 0x10, KEY_SHUFFLE }, /* Amostra */ - - /* FIXME: The keys bellow aren't ok */ - - { 0x43, KEY_CHANNELUP }, - { 0x42, KEY_CHANNELDOWN }, - { 0x1f, KEY_VOLUMEUP }, - { 0x1e, KEY_VOLUMEDOWN }, - { 0x0c, KEY_ENTER }, - - { 0x14, KEY_MUTE }, - { 0x08, KEY_AUDIO }, - - { 0x03, KEY_TEXT }, - { 0x04, KEY_EPG }, - { 0x2b, KEY_TV2 }, /* TV2 */ - - { 0x1d, KEY_RED }, - { 0x1c, KEY_YELLOW }, - { 0x41, KEY_GREEN }, - { 0x40, KEY_BLUE }, - - { 0x1a, KEY_PLAYPAUSE }, - { 0x19, KEY_RECORD }, - { 0x18, KEY_PLAY }, - { 0x1b, KEY_STOP }, -}; - -struct ir_scancode_table ir_codes_avermedia_m135a_table = { - .scan = ir_codes_avermedia_m135a, - .size = ARRAY_SIZE(ir_codes_avermedia_m135a), -}; -EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a_table); - -/* Oldrich Jedlicka <oldium.pro@seznam.cz> */ -static struct ir_scancode ir_codes_avermedia_cardbus[] = { - { 0x00, KEY_POWER }, - { 0x01, KEY_TUNER }, /* TV/FM */ - { 0x03, KEY_TEXT }, /* Teletext */ - { 0x04, KEY_EPG }, - { 0x05, KEY_1 }, - { 0x06, KEY_2 }, - { 0x07, KEY_3 }, - { 0x08, KEY_AUDIO }, - { 0x09, KEY_4 }, - { 0x0a, KEY_5 }, - { 0x0b, KEY_6 }, - { 0x0c, KEY_ZOOM }, /* Full screen */ - { 0x0d, KEY_7 }, - { 0x0e, KEY_8 }, - { 0x0f, KEY_9 }, - { 0x10, KEY_PAGEUP }, /* 16-CH PREV */ - { 0x11, KEY_0 }, - { 0x12, KEY_INFO }, - { 0x13, KEY_AGAIN }, /* CH RTN - channel return */ - { 0x14, KEY_MUTE }, - { 0x15, KEY_EDIT }, /* Autoscan */ - { 0x17, KEY_SAVE }, /* Screenshot */ - { 0x18, KEY_PLAYPAUSE }, - { 0x19, KEY_RECORD }, - { 0x1a, KEY_PLAY }, - { 0x1b, KEY_STOP }, - { 0x1c, KEY_FASTFORWARD }, - { 0x1d, KEY_REWIND }, - { 0x1e, KEY_VOLUMEDOWN }, - { 0x1f, KEY_VOLUMEUP }, - { 0x22, KEY_SLEEP }, /* Sleep */ - { 0x23, KEY_ZOOM }, /* Aspect */ - { 0x26, KEY_SCREEN }, /* Pos */ - { 0x27, KEY_ANGLE }, /* Size */ - { 0x28, KEY_SELECT }, /* Select */ - { 0x29, KEY_BLUE }, /* Blue/Picture */ - { 0x2a, KEY_BACKSPACE }, /* Back */ - { 0x2b, KEY_MEDIA }, /* PIP (Picture-in-picture) */ - { 0x2c, KEY_DOWN }, - { 0x2e, KEY_DOT }, - { 0x2f, KEY_TV }, /* Live TV */ - { 0x32, KEY_LEFT }, - { 0x33, KEY_CLEAR }, /* Clear */ - { 0x35, KEY_RED }, /* Red/TV */ - { 0x36, KEY_UP }, - { 0x37, KEY_HOME }, /* Home */ - { 0x39, KEY_GREEN }, /* Green/Video */ - { 0x3d, KEY_YELLOW }, /* Yellow/Music */ - { 0x3e, KEY_OK }, /* Ok */ - { 0x3f, KEY_RIGHT }, - { 0x40, KEY_NEXT }, /* Next */ - { 0x41, KEY_PREVIOUS }, /* Previous */ - { 0x42, KEY_CHANNELDOWN }, /* Channel down */ - { 0x43, KEY_CHANNELUP }, /* Channel up */ -}; - -struct ir_scancode_table ir_codes_avermedia_cardbus_table = { - .scan = ir_codes_avermedia_cardbus, - .size = ARRAY_SIZE(ir_codes_avermedia_cardbus), -}; -EXPORT_SYMBOL_GPL(ir_codes_avermedia_cardbus_table); - -/* Attila Kondoros <attila.kondoros@chello.hu> */ -static struct ir_scancode ir_codes_apac_viewcomp[] = { - - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - { 0x00, KEY_0 }, - { 0x17, KEY_LAST }, /* +100 */ - { 0x0a, KEY_LIST }, /* recall */ - - - { 0x1c, KEY_TUNER }, /* TV/FM */ - { 0x15, KEY_SEARCH }, /* scan */ - { 0x12, KEY_POWER }, /* power */ - { 0x1f, KEY_VOLUMEDOWN }, /* vol up */ - { 0x1b, KEY_VOLUMEUP }, /* vol down */ - { 0x1e, KEY_CHANNELDOWN }, /* chn up */ - { 0x1a, KEY_CHANNELUP }, /* chn down */ - - { 0x11, KEY_VIDEO }, /* video */ - { 0x0f, KEY_ZOOM }, /* full screen */ - { 0x13, KEY_MUTE }, /* mute/unmute */ - { 0x10, KEY_TEXT }, /* min */ - - { 0x0d, KEY_STOP }, /* freeze */ - { 0x0e, KEY_RECORD }, /* record */ - { 0x1d, KEY_PLAYPAUSE }, /* stop */ - { 0x19, KEY_PLAY }, /* play */ - - { 0x16, KEY_GOTO }, /* osd */ - { 0x14, KEY_REFRESH }, /* default */ - { 0x0c, KEY_KPPLUS }, /* fine tune >>>> */ - { 0x18, KEY_KPMINUS }, /* fine tune <<<< */ -}; - -struct ir_scancode_table ir_codes_apac_viewcomp_table = { - .scan = ir_codes_apac_viewcomp, - .size = ARRAY_SIZE(ir_codes_apac_viewcomp), -}; -EXPORT_SYMBOL_GPL(ir_codes_apac_viewcomp_table); - -/* ---------------------------------------------------------------------- */ - -static struct ir_scancode ir_codes_pixelview[] = { - - { 0x1e, KEY_POWER }, /* power */ - { 0x07, KEY_MEDIA }, /* source */ - { 0x1c, KEY_SEARCH }, /* scan */ - - - { 0x03, KEY_TUNER }, /* TV/FM */ - - { 0x00, KEY_RECORD }, - { 0x08, KEY_STOP }, - { 0x11, KEY_PLAY }, - - { 0x1a, KEY_PLAYPAUSE }, /* freeze */ - { 0x19, KEY_ZOOM }, /* zoom */ - { 0x0f, KEY_TEXT }, /* min */ - - { 0x01, KEY_1 }, - { 0x0b, KEY_2 }, - { 0x1b, KEY_3 }, - { 0x05, KEY_4 }, - { 0x09, KEY_5 }, - { 0x15, KEY_6 }, - { 0x06, KEY_7 }, - { 0x0a, KEY_8 }, - { 0x12, KEY_9 }, - { 0x02, KEY_0 }, - { 0x10, KEY_LAST }, /* +100 */ - { 0x13, KEY_LIST }, /* recall */ - - { 0x1f, KEY_CHANNELUP }, /* chn down */ - { 0x17, KEY_CHANNELDOWN }, /* chn up */ - { 0x16, KEY_VOLUMEUP }, /* vol down */ - { 0x14, KEY_VOLUMEDOWN }, /* vol up */ - - { 0x04, KEY_KPMINUS }, /* <<< */ - { 0x0e, KEY_SETUP }, /* function */ - { 0x0c, KEY_KPPLUS }, /* >>> */ - - { 0x0d, KEY_GOTO }, /* mts */ - { 0x1d, KEY_REFRESH }, /* reset */ - { 0x18, KEY_MUTE }, /* mute/unmute */ -}; - -struct ir_scancode_table ir_codes_pixelview_table = { - .scan = ir_codes_pixelview, - .size = ARRAY_SIZE(ir_codes_pixelview), -}; -EXPORT_SYMBOL_GPL(ir_codes_pixelview_table); - -/* - Mauro Carvalho Chehab <mchehab@infradead.org> - present on PV MPEG 8000GT - */ -static struct ir_scancode ir_codes_pixelview_new[] = { - { 0x3c, KEY_TIME }, /* Timeshift */ - { 0x12, KEY_POWER }, - - { 0x3d, KEY_1 }, - { 0x38, KEY_2 }, - { 0x18, KEY_3 }, - { 0x35, KEY_4 }, - { 0x39, KEY_5 }, - { 0x15, KEY_6 }, - { 0x36, KEY_7 }, - { 0x3a, KEY_8 }, - { 0x1e, KEY_9 }, - { 0x3e, KEY_0 }, - - { 0x1c, KEY_AGAIN }, /* LOOP */ - { 0x3f, KEY_MEDIA }, /* Source */ - { 0x1f, KEY_LAST }, /* +100 */ - { 0x1b, KEY_MUTE }, - - { 0x17, KEY_CHANNELDOWN }, - { 0x16, KEY_CHANNELUP }, - { 0x10, KEY_VOLUMEUP }, - { 0x14, KEY_VOLUMEDOWN }, - { 0x13, KEY_ZOOM }, - - { 0x19, KEY_CAMERA }, /* SNAPSHOT */ - { 0x1a, KEY_SEARCH }, /* scan */ - - { 0x37, KEY_REWIND }, /* << */ - { 0x32, KEY_RECORD }, /* o (red) */ - { 0x33, KEY_FORWARD }, /* >> */ - { 0x11, KEY_STOP }, /* square */ - { 0x3b, KEY_PLAY }, /* > */ - { 0x30, KEY_PLAYPAUSE }, /* || */ - - { 0x31, KEY_TV }, - { 0x34, KEY_RADIO }, -}; - -struct ir_scancode_table ir_codes_pixelview_new_table = { - .scan = ir_codes_pixelview_new, - .size = ARRAY_SIZE(ir_codes_pixelview_new), -}; -EXPORT_SYMBOL_GPL(ir_codes_pixelview_new_table); - -static struct ir_scancode ir_codes_nebula[] = { - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - { 0x0a, KEY_TV }, - { 0x0b, KEY_AUX }, - { 0x0c, KEY_DVD }, - { 0x0d, KEY_POWER }, - { 0x0e, KEY_MHP }, /* labelled 'Picture' */ - { 0x0f, KEY_AUDIO }, - { 0x10, KEY_INFO }, - { 0x11, KEY_F13 }, /* 16:9 */ - { 0x12, KEY_F14 }, /* 14:9 */ - { 0x13, KEY_EPG }, - { 0x14, KEY_EXIT }, - { 0x15, KEY_MENU }, - { 0x16, KEY_UP }, - { 0x17, KEY_DOWN }, - { 0x18, KEY_LEFT }, - { 0x19, KEY_RIGHT }, - { 0x1a, KEY_ENTER }, - { 0x1b, KEY_CHANNELUP }, - { 0x1c, KEY_CHANNELDOWN }, - { 0x1d, KEY_VOLUMEUP }, - { 0x1e, KEY_VOLUMEDOWN }, - { 0x1f, KEY_RED }, - { 0x20, KEY_GREEN }, - { 0x21, KEY_YELLOW }, - { 0x22, KEY_BLUE }, - { 0x23, KEY_SUBTITLE }, - { 0x24, KEY_F15 }, /* AD */ - { 0x25, KEY_TEXT }, - { 0x26, KEY_MUTE }, - { 0x27, KEY_REWIND }, - { 0x28, KEY_STOP }, - { 0x29, KEY_PLAY }, - { 0x2a, KEY_FASTFORWARD }, - { 0x2b, KEY_F16 }, /* chapter */ - { 0x2c, KEY_PAUSE }, - { 0x2d, KEY_PLAY }, - { 0x2e, KEY_RECORD }, - { 0x2f, KEY_F17 }, /* picture in picture */ - { 0x30, KEY_KPPLUS }, /* zoom in */ - { 0x31, KEY_KPMINUS }, /* zoom out */ - { 0x32, KEY_F18 }, /* capture */ - { 0x33, KEY_F19 }, /* web */ - { 0x34, KEY_EMAIL }, - { 0x35, KEY_PHONE }, - { 0x36, KEY_PC }, -}; - -struct ir_scancode_table ir_codes_nebula_table = { - .scan = ir_codes_nebula, - .size = ARRAY_SIZE(ir_codes_nebula), -}; -EXPORT_SYMBOL_GPL(ir_codes_nebula_table); - -/* DigitalNow DNTV Live DVB-T Remote */ -static struct ir_scancode ir_codes_dntv_live_dvb_t[] = { - { 0x00, KEY_ESC }, /* 'go up a level?' */ - /* Keys 0 to 9 */ - { 0x0a, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x0b, KEY_TUNER }, /* tv/fm */ - { 0x0c, KEY_SEARCH }, /* scan */ - { 0x0d, KEY_STOP }, - { 0x0e, KEY_PAUSE }, - { 0x0f, KEY_LIST }, /* source */ - - { 0x10, KEY_MUTE }, - { 0x11, KEY_REWIND }, /* backward << */ - { 0x12, KEY_POWER }, - { 0x13, KEY_CAMERA }, /* snap */ - { 0x14, KEY_AUDIO }, /* stereo */ - { 0x15, KEY_CLEAR }, /* reset */ - { 0x16, KEY_PLAY }, - { 0x17, KEY_ENTER }, - { 0x18, KEY_ZOOM }, /* full screen */ - { 0x19, KEY_FASTFORWARD }, /* forward >> */ - { 0x1a, KEY_CHANNELUP }, - { 0x1b, KEY_VOLUMEUP }, - { 0x1c, KEY_INFO }, /* preview */ - { 0x1d, KEY_RECORD }, /* record */ - { 0x1e, KEY_CHANNELDOWN }, - { 0x1f, KEY_VOLUMEDOWN }, -}; - -struct ir_scancode_table ir_codes_dntv_live_dvb_t_table = { - .scan = ir_codes_dntv_live_dvb_t, - .size = ARRAY_SIZE(ir_codes_dntv_live_dvb_t), -}; -EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvb_t_table); - -/* ---------------------------------------------------------------------- */ - -/* IO-DATA BCTV7E Remote */ -static struct ir_scancode ir_codes_iodata_bctv7e[] = { - { 0x40, KEY_TV }, - { 0x20, KEY_RADIO }, /* FM */ - { 0x60, KEY_EPG }, - { 0x00, KEY_POWER }, - - /* Keys 0 to 9 */ - { 0x44, KEY_0 }, /* 10 */ - { 0x50, KEY_1 }, - { 0x30, KEY_2 }, - { 0x70, KEY_3 }, - { 0x48, KEY_4 }, - { 0x28, KEY_5 }, - { 0x68, KEY_6 }, - { 0x58, KEY_7 }, - { 0x38, KEY_8 }, - { 0x78, KEY_9 }, - - { 0x10, KEY_L }, /* Live */ - { 0x08, KEY_TIME }, /* Time Shift */ - - { 0x18, KEY_PLAYPAUSE }, /* Play */ - - { 0x24, KEY_ENTER }, /* 11 */ - { 0x64, KEY_ESC }, /* 12 */ - { 0x04, KEY_M }, /* Multi */ - - { 0x54, KEY_VIDEO }, - { 0x34, KEY_CHANNELUP }, - { 0x74, KEY_VOLUMEUP }, - { 0x14, KEY_MUTE }, - - { 0x4c, KEY_VCR }, /* SVIDEO */ - { 0x2c, KEY_CHANNELDOWN }, - { 0x6c, KEY_VOLUMEDOWN }, - { 0x0c, KEY_ZOOM }, - - { 0x5c, KEY_PAUSE }, - { 0x3c, KEY_RED }, /* || (red) */ - { 0x7c, KEY_RECORD }, /* recording */ - { 0x1c, KEY_STOP }, - - { 0x41, KEY_REWIND }, /* backward << */ - { 0x21, KEY_PLAY }, - { 0x61, KEY_FASTFORWARD }, /* forward >> */ - { 0x01, KEY_NEXT }, /* skip >| */ -}; - -struct ir_scancode_table ir_codes_iodata_bctv7e_table = { - .scan = ir_codes_iodata_bctv7e, - .size = ARRAY_SIZE(ir_codes_iodata_bctv7e), -}; -EXPORT_SYMBOL_GPL(ir_codes_iodata_bctv7e_table); - -/* ---------------------------------------------------------------------- */ - -/* ADS Tech Instant TV DVB-T PCI Remote */ -static struct ir_scancode ir_codes_adstech_dvb_t_pci[] = { - /* Keys 0 to 9 */ - { 0x4d, KEY_0 }, - { 0x57, KEY_1 }, - { 0x4f, KEY_2 }, - { 0x53, KEY_3 }, - { 0x56, KEY_4 }, - { 0x4e, KEY_5 }, - { 0x5e, KEY_6 }, - { 0x54, KEY_7 }, - { 0x4c, KEY_8 }, - { 0x5c, KEY_9 }, - - { 0x5b, KEY_POWER }, - { 0x5f, KEY_MUTE }, - { 0x55, KEY_GOTO }, - { 0x5d, KEY_SEARCH }, - { 0x17, KEY_EPG }, /* Guide */ - { 0x1f, KEY_MENU }, - { 0x0f, KEY_UP }, - { 0x46, KEY_DOWN }, - { 0x16, KEY_LEFT }, - { 0x1e, KEY_RIGHT }, - { 0x0e, KEY_SELECT }, /* Enter */ - { 0x5a, KEY_INFO }, - { 0x52, KEY_EXIT }, - { 0x59, KEY_PREVIOUS }, - { 0x51, KEY_NEXT }, - { 0x58, KEY_REWIND }, - { 0x50, KEY_FORWARD }, - { 0x44, KEY_PLAYPAUSE }, - { 0x07, KEY_STOP }, - { 0x1b, KEY_RECORD }, - { 0x13, KEY_TUNER }, /* Live */ - { 0x0a, KEY_A }, - { 0x12, KEY_B }, - { 0x03, KEY_PROG1 }, /* 1 */ - { 0x01, KEY_PROG2 }, /* 2 */ - { 0x00, KEY_PROG3 }, /* 3 */ - { 0x06, KEY_DVD }, - { 0x48, KEY_AUX }, /* Photo */ - { 0x40, KEY_VIDEO }, - { 0x19, KEY_AUDIO }, /* Music */ - { 0x0b, KEY_CHANNELUP }, - { 0x08, KEY_CHANNELDOWN }, - { 0x15, KEY_VOLUMEUP }, - { 0x1c, KEY_VOLUMEDOWN }, -}; - -struct ir_scancode_table ir_codes_adstech_dvb_t_pci_table = { - .scan = ir_codes_adstech_dvb_t_pci, - .size = ARRAY_SIZE(ir_codes_adstech_dvb_t_pci), -}; -EXPORT_SYMBOL_GPL(ir_codes_adstech_dvb_t_pci_table); - -/* ---------------------------------------------------------------------- */ - -/* MSI TV@nywhere MASTER remote */ - -static struct ir_scancode ir_codes_msi_tvanywhere[] = { - /* Keys 0 to 9 */ - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x0c, KEY_MUTE }, - { 0x0f, KEY_SCREEN }, /* Full Screen */ - { 0x10, KEY_FN }, /* Funtion */ - { 0x11, KEY_TIME }, /* Time shift */ - { 0x12, KEY_POWER }, - { 0x13, KEY_MEDIA }, /* MTS */ - { 0x14, KEY_SLOW }, - { 0x16, KEY_REWIND }, /* backward << */ - { 0x17, KEY_ENTER }, /* Return */ - { 0x18, KEY_FASTFORWARD }, /* forward >> */ - { 0x1a, KEY_CHANNELUP }, - { 0x1b, KEY_VOLUMEUP }, - { 0x1e, KEY_CHANNELDOWN }, - { 0x1f, KEY_VOLUMEDOWN }, -}; - -struct ir_scancode_table ir_codes_msi_tvanywhere_table = { - .scan = ir_codes_msi_tvanywhere, - .size = ARRAY_SIZE(ir_codes_msi_tvanywhere), -}; -EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_table); - -/* ---------------------------------------------------------------------- */ - -/* - Keycodes for remote on the MSI TV@nywhere Plus. The controller IC on the card - is marked "KS003". The controller is I2C at address 0x30, but does not seem - to respond to probes until a read is performed from a valid device. - I don't know why... - - Note: This remote may be of similar or identical design to the - Pixelview remote (?). The raw codes and duplicate button codes - appear to be the same. - - Henry Wong <henry@stuffedcow.net> - Some changes to formatting and keycodes by Mark Schultz <n9xmj@yahoo.com> - -*/ - -static struct ir_scancode ir_codes_msi_tvanywhere_plus[] = { - -/* ---- Remote Button Layout ---- - - POWER SOURCE SCAN MUTE - TV/FM 1 2 3 - |> 4 5 6 - <| 7 8 9 - ^^UP 0 + RECALL - vvDN RECORD STOP PLAY - - MINIMIZE ZOOM - - CH+ - VOL- VOL+ - CH- - - SNAPSHOT MTS - - << FUNC >> RESET -*/ - - { 0x01, KEY_1 }, /* 1 */ - { 0x0b, KEY_2 }, /* 2 */ - { 0x1b, KEY_3 }, /* 3 */ - { 0x05, KEY_4 }, /* 4 */ - { 0x09, KEY_5 }, /* 5 */ - { 0x15, KEY_6 }, /* 6 */ - { 0x06, KEY_7 }, /* 7 */ - { 0x0a, KEY_8 }, /* 8 */ - { 0x12, KEY_9 }, /* 9 */ - { 0x02, KEY_0 }, /* 0 */ - { 0x10, KEY_KPPLUS }, /* + */ - { 0x13, KEY_AGAIN }, /* Recall */ - - { 0x1e, KEY_POWER }, /* Power */ - { 0x07, KEY_TUNER }, /* Source */ - { 0x1c, KEY_SEARCH }, /* Scan */ - { 0x18, KEY_MUTE }, /* Mute */ - - { 0x03, KEY_RADIO }, /* TV/FM */ - /* The next four keys are duplicates that appear to send the - same IR code as Ch+, Ch-, >>, and << . The raw code assigned - to them is the actual code + 0x20 - they will never be - detected as such unless some way is discovered to distinguish - these buttons from those that have the same code. */ - { 0x3f, KEY_RIGHT }, /* |> and Ch+ */ - { 0x37, KEY_LEFT }, /* <| and Ch- */ - { 0x2c, KEY_UP }, /* ^^Up and >> */ - { 0x24, KEY_DOWN }, /* vvDn and << */ - - { 0x00, KEY_RECORD }, /* Record */ - { 0x08, KEY_STOP }, /* Stop */ - { 0x11, KEY_PLAY }, /* Play */ - - { 0x0f, KEY_CLOSE }, /* Minimize */ - { 0x19, KEY_ZOOM }, /* Zoom */ - { 0x1a, KEY_CAMERA }, /* Snapshot */ - { 0x0d, KEY_LANGUAGE }, /* MTS */ - - { 0x14, KEY_VOLUMEDOWN }, /* Vol- */ - { 0x16, KEY_VOLUMEUP }, /* Vol+ */ - { 0x17, KEY_CHANNELDOWN }, /* Ch- */ - { 0x1f, KEY_CHANNELUP }, /* Ch+ */ - - { 0x04, KEY_REWIND }, /* << */ - { 0x0e, KEY_MENU }, /* Function */ - { 0x0c, KEY_FASTFORWARD }, /* >> */ - { 0x1d, KEY_RESTART }, /* Reset */ -}; - -struct ir_scancode_table ir_codes_msi_tvanywhere_plus_table = { - .scan = ir_codes_msi_tvanywhere_plus, - .size = ARRAY_SIZE(ir_codes_msi_tvanywhere_plus), -}; -EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_plus_table); - -/* ---------------------------------------------------------------------- */ - -/* Cinergy 1400 DVB-T */ -static struct ir_scancode ir_codes_cinergy_1400[] = { - { 0x01, KEY_POWER }, - { 0x02, KEY_1 }, - { 0x03, KEY_2 }, - { 0x04, KEY_3 }, - { 0x05, KEY_4 }, - { 0x06, KEY_5 }, - { 0x07, KEY_6 }, - { 0x08, KEY_7 }, - { 0x09, KEY_8 }, - { 0x0a, KEY_9 }, - { 0x0c, KEY_0 }, - - { 0x0b, KEY_VIDEO }, - { 0x0d, KEY_REFRESH }, - { 0x0e, KEY_SELECT }, - { 0x0f, KEY_EPG }, - { 0x10, KEY_UP }, - { 0x11, KEY_LEFT }, - { 0x12, KEY_OK }, - { 0x13, KEY_RIGHT }, - { 0x14, KEY_DOWN }, - { 0x15, KEY_TEXT }, - { 0x16, KEY_INFO }, - - { 0x17, KEY_RED }, - { 0x18, KEY_GREEN }, - { 0x19, KEY_YELLOW }, - { 0x1a, KEY_BLUE }, - - { 0x1b, KEY_CHANNELUP }, - { 0x1c, KEY_VOLUMEUP }, - { 0x1d, KEY_MUTE }, - { 0x1e, KEY_VOLUMEDOWN }, - { 0x1f, KEY_CHANNELDOWN }, - - { 0x40, KEY_PAUSE }, - { 0x4c, KEY_PLAY }, - { 0x58, KEY_RECORD }, - { 0x54, KEY_PREVIOUS }, - { 0x48, KEY_STOP }, - { 0x5c, KEY_NEXT }, -}; - -struct ir_scancode_table ir_codes_cinergy_1400_table = { - .scan = ir_codes_cinergy_1400, - .size = ARRAY_SIZE(ir_codes_cinergy_1400), -}; -EXPORT_SYMBOL_GPL(ir_codes_cinergy_1400_table); - -/* ---------------------------------------------------------------------- */ - -/* AVERTV STUDIO 303 Remote */ -static struct ir_scancode ir_codes_avertv_303[] = { - { 0x2a, KEY_1 }, - { 0x32, KEY_2 }, - { 0x3a, KEY_3 }, - { 0x4a, KEY_4 }, - { 0x52, KEY_5 }, - { 0x5a, KEY_6 }, - { 0x6a, KEY_7 }, - { 0x72, KEY_8 }, - { 0x7a, KEY_9 }, - { 0x0e, KEY_0 }, - - { 0x02, KEY_POWER }, - { 0x22, KEY_VIDEO }, - { 0x42, KEY_AUDIO }, - { 0x62, KEY_ZOOM }, - { 0x0a, KEY_TV }, - { 0x12, KEY_CD }, - { 0x1a, KEY_TEXT }, - - { 0x16, KEY_SUBTITLE }, - { 0x1e, KEY_REWIND }, - { 0x06, KEY_PRINT }, - - { 0x2e, KEY_SEARCH }, - { 0x36, KEY_SLEEP }, - { 0x3e, KEY_SHUFFLE }, - { 0x26, KEY_MUTE }, - - { 0x4e, KEY_RECORD }, - { 0x56, KEY_PAUSE }, - { 0x5e, KEY_STOP }, - { 0x46, KEY_PLAY }, - - { 0x6e, KEY_RED }, - { 0x0b, KEY_GREEN }, - { 0x66, KEY_YELLOW }, - { 0x03, KEY_BLUE }, - - { 0x76, KEY_LEFT }, - { 0x7e, KEY_RIGHT }, - { 0x13, KEY_DOWN }, - { 0x1b, KEY_UP }, -}; - -struct ir_scancode_table ir_codes_avertv_303_table = { - .scan = ir_codes_avertv_303, - .size = ARRAY_SIZE(ir_codes_avertv_303), -}; -EXPORT_SYMBOL_GPL(ir_codes_avertv_303_table); - -/* ---------------------------------------------------------------------- */ - -/* DigitalNow DNTV Live! DVB-T Pro Remote */ -static struct ir_scancode ir_codes_dntv_live_dvbt_pro[] = { - { 0x16, KEY_POWER }, - { 0x5b, KEY_HOME }, - - { 0x55, KEY_TV }, /* live tv */ - { 0x58, KEY_TUNER }, /* digital Radio */ - { 0x5a, KEY_RADIO }, /* FM radio */ - { 0x59, KEY_DVD }, /* dvd menu */ - { 0x03, KEY_1 }, - { 0x01, KEY_2 }, - { 0x06, KEY_3 }, - { 0x09, KEY_4 }, - { 0x1d, KEY_5 }, - { 0x1f, KEY_6 }, - { 0x0d, KEY_7 }, - { 0x19, KEY_8 }, - { 0x1b, KEY_9 }, - { 0x0c, KEY_CANCEL }, - { 0x15, KEY_0 }, - { 0x4a, KEY_CLEAR }, - { 0x13, KEY_BACK }, - { 0x00, KEY_TAB }, - { 0x4b, KEY_UP }, - { 0x4e, KEY_LEFT }, - { 0x4f, KEY_OK }, - { 0x52, KEY_RIGHT }, - { 0x51, KEY_DOWN }, - { 0x1e, KEY_VOLUMEUP }, - { 0x0a, KEY_VOLUMEDOWN }, - { 0x02, KEY_CHANNELDOWN }, - { 0x05, KEY_CHANNELUP }, - { 0x11, KEY_RECORD }, - { 0x14, KEY_PLAY }, - { 0x4c, KEY_PAUSE }, - { 0x1a, KEY_STOP }, - { 0x40, KEY_REWIND }, - { 0x12, KEY_FASTFORWARD }, - { 0x41, KEY_PREVIOUSSONG }, /* replay |< */ - { 0x42, KEY_NEXTSONG }, /* skip >| */ - { 0x54, KEY_CAMERA }, /* capture */ - { 0x50, KEY_LANGUAGE }, /* sap */ - { 0x47, KEY_TV2 }, /* pip */ - { 0x4d, KEY_SCREEN }, - { 0x43, KEY_SUBTITLE }, - { 0x10, KEY_MUTE }, - { 0x49, KEY_AUDIO }, /* l/r */ - { 0x07, KEY_SLEEP }, - { 0x08, KEY_VIDEO }, /* a/v */ - { 0x0e, KEY_PREVIOUS }, /* recall */ - { 0x45, KEY_ZOOM }, /* zoom + */ - { 0x46, KEY_ANGLE }, /* zoom - */ - { 0x56, KEY_RED }, - { 0x57, KEY_GREEN }, - { 0x5c, KEY_YELLOW }, - { 0x5d, KEY_BLUE }, -}; - -struct ir_scancode_table ir_codes_dntv_live_dvbt_pro_table = { - .scan = ir_codes_dntv_live_dvbt_pro, - .size = ARRAY_SIZE(ir_codes_dntv_live_dvbt_pro), -}; -EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvbt_pro_table); - -static struct ir_scancode ir_codes_em_terratec[] = { - { 0x01, KEY_CHANNEL }, - { 0x02, KEY_SELECT }, - { 0x03, KEY_MUTE }, - { 0x04, KEY_POWER }, - { 0x05, KEY_1 }, - { 0x06, KEY_2 }, - { 0x07, KEY_3 }, - { 0x08, KEY_CHANNELUP }, - { 0x09, KEY_4 }, - { 0x0a, KEY_5 }, - { 0x0b, KEY_6 }, - { 0x0c, KEY_CHANNELDOWN }, - { 0x0d, KEY_7 }, - { 0x0e, KEY_8 }, - { 0x0f, KEY_9 }, - { 0x10, KEY_VOLUMEUP }, - { 0x11, KEY_0 }, - { 0x12, KEY_MENU }, - { 0x13, KEY_PRINT }, - { 0x14, KEY_VOLUMEDOWN }, - { 0x16, KEY_PAUSE }, - { 0x18, KEY_RECORD }, - { 0x19, KEY_REWIND }, - { 0x1a, KEY_PLAY }, - { 0x1b, KEY_FORWARD }, - { 0x1c, KEY_BACKSPACE }, - { 0x1e, KEY_STOP }, - { 0x40, KEY_ZOOM }, -}; - -struct ir_scancode_table ir_codes_em_terratec_table = { - .scan = ir_codes_em_terratec, - .size = ARRAY_SIZE(ir_codes_em_terratec), -}; -EXPORT_SYMBOL_GPL(ir_codes_em_terratec_table); - -static struct ir_scancode ir_codes_pinnacle_grey[] = { - { 0x3a, KEY_0 }, - { 0x31, KEY_1 }, - { 0x32, KEY_2 }, - { 0x33, KEY_3 }, - { 0x34, KEY_4 }, - { 0x35, KEY_5 }, - { 0x36, KEY_6 }, - { 0x37, KEY_7 }, - { 0x38, KEY_8 }, - { 0x39, KEY_9 }, - - { 0x2f, KEY_POWER }, - - { 0x2e, KEY_P }, - { 0x1f, KEY_L }, - { 0x2b, KEY_I }, - - { 0x2d, KEY_SCREEN }, - { 0x1e, KEY_ZOOM }, - { 0x1b, KEY_VOLUMEUP }, - { 0x0f, KEY_VOLUMEDOWN }, - { 0x17, KEY_CHANNELUP }, - { 0x1c, KEY_CHANNELDOWN }, - { 0x25, KEY_INFO }, - - { 0x3c, KEY_MUTE }, - - { 0x3d, KEY_LEFT }, - { 0x3b, KEY_RIGHT }, - - { 0x3f, KEY_UP }, - { 0x3e, KEY_DOWN }, - { 0x1a, KEY_ENTER }, - - { 0x1d, KEY_MENU }, - { 0x19, KEY_AGAIN }, - { 0x16, KEY_PREVIOUSSONG }, - { 0x13, KEY_NEXTSONG }, - { 0x15, KEY_PAUSE }, - { 0x0e, KEY_REWIND }, - { 0x0d, KEY_PLAY }, - { 0x0b, KEY_STOP }, - { 0x07, KEY_FORWARD }, - { 0x27, KEY_RECORD }, - { 0x26, KEY_TUNER }, - { 0x29, KEY_TEXT }, - { 0x2a, KEY_MEDIA }, - { 0x18, KEY_EPG }, -}; - -struct ir_scancode_table ir_codes_pinnacle_grey_table = { - .scan = ir_codes_pinnacle_grey, - .size = ARRAY_SIZE(ir_codes_pinnacle_grey), -}; -EXPORT_SYMBOL_GPL(ir_codes_pinnacle_grey_table); - -static struct ir_scancode ir_codes_flyvideo[] = { - { 0x0f, KEY_0 }, - { 0x03, KEY_1 }, - { 0x04, KEY_2 }, - { 0x05, KEY_3 }, - { 0x07, KEY_4 }, - { 0x08, KEY_5 }, - { 0x09, KEY_6 }, - { 0x0b, KEY_7 }, - { 0x0c, KEY_8 }, - { 0x0d, KEY_9 }, - - { 0x0e, KEY_MODE }, /* Air/Cable */ - { 0x11, KEY_VIDEO }, /* Video */ - { 0x15, KEY_AUDIO }, /* Audio */ - { 0x00, KEY_POWER }, /* Power */ - { 0x18, KEY_TUNER }, /* AV Source */ - { 0x02, KEY_ZOOM }, /* Fullscreen */ - { 0x1a, KEY_LANGUAGE }, /* Stereo */ - { 0x1b, KEY_MUTE }, /* Mute */ - { 0x14, KEY_VOLUMEUP }, /* Volume + */ - { 0x17, KEY_VOLUMEDOWN },/* Volume - */ - { 0x12, KEY_CHANNELUP },/* Channel + */ - { 0x13, KEY_CHANNELDOWN },/* Channel - */ - { 0x06, KEY_AGAIN }, /* Recall */ - { 0x10, KEY_ENTER }, /* Enter */ - - { 0x19, KEY_BACK }, /* Rewind ( <<< ) */ - { 0x1f, KEY_FORWARD }, /* Forward ( >>> ) */ - { 0x0a, KEY_ANGLE }, /* no label, may be used as the PAUSE button */ -}; - -struct ir_scancode_table ir_codes_flyvideo_table = { - .scan = ir_codes_flyvideo, - .size = ARRAY_SIZE(ir_codes_flyvideo), -}; -EXPORT_SYMBOL_GPL(ir_codes_flyvideo_table); - -static struct ir_scancode ir_codes_flydvb[] = { - { 0x01, KEY_ZOOM }, /* Full Screen */ - { 0x00, KEY_POWER }, /* Power */ - - { 0x03, KEY_1 }, - { 0x04, KEY_2 }, - { 0x05, KEY_3 }, - { 0x07, KEY_4 }, - { 0x08, KEY_5 }, - { 0x09, KEY_6 }, - { 0x0b, KEY_7 }, - { 0x0c, KEY_8 }, - { 0x0d, KEY_9 }, - { 0x06, KEY_AGAIN }, /* Recall */ - { 0x0f, KEY_0 }, - { 0x10, KEY_MUTE }, /* Mute */ - { 0x02, KEY_RADIO }, /* TV/Radio */ - { 0x1b, KEY_LANGUAGE }, /* SAP (Second Audio Program) */ - - { 0x14, KEY_VOLUMEUP }, /* VOL+ */ - { 0x17, KEY_VOLUMEDOWN }, /* VOL- */ - { 0x12, KEY_CHANNELUP }, /* CH+ */ - { 0x13, KEY_CHANNELDOWN }, /* CH- */ - { 0x1d, KEY_ENTER }, /* Enter */ - - { 0x1a, KEY_MODE }, /* PIP */ - { 0x18, KEY_TUNER }, /* Source */ - - { 0x1e, KEY_RECORD }, /* Record/Pause */ - { 0x15, KEY_ANGLE }, /* Swap (no label on key) */ - { 0x1c, KEY_PAUSE }, /* Timeshift/Pause */ - { 0x19, KEY_BACK }, /* Rewind << */ - { 0x0a, KEY_PLAYPAUSE }, /* Play/Pause */ - { 0x1f, KEY_FORWARD }, /* Forward >> */ - { 0x16, KEY_PREVIOUS }, /* Back |<< */ - { 0x11, KEY_STOP }, /* Stop */ - { 0x0e, KEY_NEXT }, /* End >>| */ -}; - -struct ir_scancode_table ir_codes_flydvb_table = { - .scan = ir_codes_flydvb, - .size = ARRAY_SIZE(ir_codes_flydvb), -}; -EXPORT_SYMBOL_GPL(ir_codes_flydvb_table); - -static struct ir_scancode ir_codes_cinergy[] = { - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x0a, KEY_POWER }, - { 0x0b, KEY_PROG1 }, /* app */ - { 0x0c, KEY_ZOOM }, /* zoom/fullscreen */ - { 0x0d, KEY_CHANNELUP }, /* channel */ - { 0x0e, KEY_CHANNELDOWN }, /* channel- */ - { 0x0f, KEY_VOLUMEUP }, - { 0x10, KEY_VOLUMEDOWN }, - { 0x11, KEY_TUNER }, /* AV */ - { 0x12, KEY_NUMLOCK }, /* -/-- */ - { 0x13, KEY_AUDIO }, /* audio */ - { 0x14, KEY_MUTE }, - { 0x15, KEY_UP }, - { 0x16, KEY_DOWN }, - { 0x17, KEY_LEFT }, - { 0x18, KEY_RIGHT }, - { 0x19, BTN_LEFT, }, - { 0x1a, BTN_RIGHT, }, - { 0x1b, KEY_WWW }, /* text */ - { 0x1c, KEY_REWIND }, - { 0x1d, KEY_FORWARD }, - { 0x1e, KEY_RECORD }, - { 0x1f, KEY_PLAY }, - { 0x20, KEY_PREVIOUSSONG }, - { 0x21, KEY_NEXTSONG }, - { 0x22, KEY_PAUSE }, - { 0x23, KEY_STOP }, -}; - -struct ir_scancode_table ir_codes_cinergy_table = { - .scan = ir_codes_cinergy, - .size = ARRAY_SIZE(ir_codes_cinergy), -}; -EXPORT_SYMBOL_GPL(ir_codes_cinergy_table); - -/* Alfons Geser <a.geser@cox.net> - * updates from Job D. R. Borges <jobdrb@ig.com.br> */ -static struct ir_scancode ir_codes_eztv[] = { - { 0x12, KEY_POWER }, - { 0x01, KEY_TV }, /* DVR */ - { 0x15, KEY_DVD }, /* DVD */ - { 0x17, KEY_AUDIO }, /* music */ - /* DVR mode / DVD mode / music mode */ - - { 0x1b, KEY_MUTE }, /* mute */ - { 0x02, KEY_LANGUAGE }, /* MTS/SAP / audio / autoseek */ - { 0x1e, KEY_SUBTITLE }, /* closed captioning / subtitle / seek */ - { 0x16, KEY_ZOOM }, /* full screen */ - { 0x1c, KEY_VIDEO }, /* video source / eject / delall */ - { 0x1d, KEY_RESTART }, /* playback / angle / del */ - { 0x2f, KEY_SEARCH }, /* scan / menu / playlist */ - { 0x30, KEY_CHANNEL }, /* CH surfing / bookmark / memo */ - - { 0x31, KEY_HELP }, /* help */ - { 0x32, KEY_MODE }, /* num/memo */ - { 0x33, KEY_ESC }, /* cancel */ - - { 0x0c, KEY_UP }, /* up */ - { 0x10, KEY_DOWN }, /* down */ - { 0x08, KEY_LEFT }, /* left */ - { 0x04, KEY_RIGHT }, /* right */ - { 0x03, KEY_SELECT }, /* select */ - - { 0x1f, KEY_REWIND }, /* rewind */ - { 0x20, KEY_PLAYPAUSE },/* play/pause */ - { 0x29, KEY_FORWARD }, /* forward */ - { 0x14, KEY_AGAIN }, /* repeat */ - { 0x2b, KEY_RECORD }, /* recording */ - { 0x2c, KEY_STOP }, /* stop */ - { 0x2d, KEY_PLAY }, /* play */ - { 0x2e, KEY_CAMERA }, /* snapshot / shuffle */ - - { 0x00, KEY_0 }, - { 0x05, KEY_1 }, - { 0x06, KEY_2 }, - { 0x07, KEY_3 }, - { 0x09, KEY_4 }, - { 0x0a, KEY_5 }, - { 0x0b, KEY_6 }, - { 0x0d, KEY_7 }, - { 0x0e, KEY_8 }, - { 0x0f, KEY_9 }, - - { 0x2a, KEY_VOLUMEUP }, - { 0x11, KEY_VOLUMEDOWN }, - { 0x18, KEY_CHANNELUP },/* CH.tracking up */ - { 0x19, KEY_CHANNELDOWN },/* CH.tracking down */ - - { 0x13, KEY_ENTER }, /* enter */ - { 0x21, KEY_DOT }, /* . (decimal dot) */ -}; - -struct ir_scancode_table ir_codes_eztv_table = { - .scan = ir_codes_eztv, - .size = ARRAY_SIZE(ir_codes_eztv), -}; -EXPORT_SYMBOL_GPL(ir_codes_eztv_table); - -/* Alex Hermann <gaaf@gmx.net> */ -static struct ir_scancode ir_codes_avermedia[] = { - { 0x28, KEY_1 }, - { 0x18, KEY_2 }, - { 0x38, KEY_3 }, - { 0x24, KEY_4 }, - { 0x14, KEY_5 }, - { 0x34, KEY_6 }, - { 0x2c, KEY_7 }, - { 0x1c, KEY_8 }, - { 0x3c, KEY_9 }, - { 0x22, KEY_0 }, - - { 0x20, KEY_TV }, /* TV/FM */ - { 0x10, KEY_CD }, /* CD */ - { 0x30, KEY_TEXT }, /* TELETEXT */ - { 0x00, KEY_POWER }, /* POWER */ - - { 0x08, KEY_VIDEO }, /* VIDEO */ - { 0x04, KEY_AUDIO }, /* AUDIO */ - { 0x0c, KEY_ZOOM }, /* FULL SCREEN */ - - { 0x12, KEY_SUBTITLE }, /* DISPLAY */ - { 0x32, KEY_REWIND }, /* LOOP */ - { 0x02, KEY_PRINT }, /* PREVIEW */ - - { 0x2a, KEY_SEARCH }, /* AUTOSCAN */ - { 0x1a, KEY_SLEEP }, /* FREEZE */ - { 0x3a, KEY_CAMERA }, /* SNAPSHOT */ - { 0x0a, KEY_MUTE }, /* MUTE */ - - { 0x26, KEY_RECORD }, /* RECORD */ - { 0x16, KEY_PAUSE }, /* PAUSE */ - { 0x36, KEY_STOP }, /* STOP */ - { 0x06, KEY_PLAY }, /* PLAY */ - - { 0x2e, KEY_RED }, /* RED */ - { 0x21, KEY_GREEN }, /* GREEN */ - { 0x0e, KEY_YELLOW }, /* YELLOW */ - { 0x01, KEY_BLUE }, /* BLUE */ - - { 0x1e, KEY_VOLUMEDOWN }, /* VOLUME- */ - { 0x3e, KEY_VOLUMEUP }, /* VOLUME+ */ - { 0x11, KEY_CHANNELDOWN }, /* CHANNEL/PAGE- */ - { 0x31, KEY_CHANNELUP } /* CHANNEL/PAGE+ */ -}; - -struct ir_scancode_table ir_codes_avermedia_table = { - .scan = ir_codes_avermedia, - .size = ARRAY_SIZE(ir_codes_avermedia), -}; -EXPORT_SYMBOL_GPL(ir_codes_avermedia_table); - -static struct ir_scancode ir_codes_videomate_tv_pvr[] = { - { 0x14, KEY_MUTE }, - { 0x24, KEY_ZOOM }, - - { 0x01, KEY_DVD }, - { 0x23, KEY_RADIO }, - { 0x00, KEY_TV }, - - { 0x0a, KEY_REWIND }, - { 0x08, KEY_PLAYPAUSE }, - { 0x0f, KEY_FORWARD }, - - { 0x02, KEY_PREVIOUS }, - { 0x07, KEY_STOP }, - { 0x06, KEY_NEXT }, - - { 0x0c, KEY_UP }, - { 0x0e, KEY_DOWN }, - { 0x0b, KEY_LEFT }, - { 0x0d, KEY_RIGHT }, - { 0x11, KEY_OK }, - - { 0x03, KEY_MENU }, - { 0x09, KEY_SETUP }, - { 0x05, KEY_VIDEO }, - { 0x22, KEY_CHANNEL }, - - { 0x12, KEY_VOLUMEUP }, - { 0x15, KEY_VOLUMEDOWN }, - { 0x10, KEY_CHANNELUP }, - { 0x13, KEY_CHANNELDOWN }, - - { 0x04, KEY_RECORD }, - - { 0x16, KEY_1 }, - { 0x17, KEY_2 }, - { 0x18, KEY_3 }, - { 0x19, KEY_4 }, - { 0x1a, KEY_5 }, - { 0x1b, KEY_6 }, - { 0x1c, KEY_7 }, - { 0x1d, KEY_8 }, - { 0x1e, KEY_9 }, - { 0x1f, KEY_0 }, - - { 0x20, KEY_LANGUAGE }, - { 0x21, KEY_SLEEP }, -}; - -struct ir_scancode_table ir_codes_videomate_tv_pvr_table = { - .scan = ir_codes_videomate_tv_pvr, - .size = ARRAY_SIZE(ir_codes_videomate_tv_pvr), -}; -EXPORT_SYMBOL_GPL(ir_codes_videomate_tv_pvr_table); - -/* Michael Tokarev <mjt@tls.msk.ru> - http://www.corpit.ru/mjt/beholdTV/remote_control.jpg - keytable is used by MANLI MTV00[0x0c] and BeholdTV 40[13] at - least, and probably other cards too. - The "ascii-art picture" below (in comments, first row - is the keycode in hex, and subsequent row(s) shows - the button labels (several variants when appropriate) - helps to descide which keycodes to assign to the buttons. - */ -static struct ir_scancode ir_codes_manli[] = { - - /* 0x1c 0x12 * - * FUNCTION POWER * - * FM (|) * - * */ - { 0x1c, KEY_RADIO }, /*XXX*/ - { 0x12, KEY_POWER }, - - /* 0x01 0x02 0x03 * - * 1 2 3 * - * * - * 0x04 0x05 0x06 * - * 4 5 6 * - * * - * 0x07 0x08 0x09 * - * 7 8 9 * - * */ - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - /* 0x0a 0x00 0x17 * - * RECALL 0 +100 * - * PLUS * - * */ - { 0x0a, KEY_AGAIN }, /*XXX KEY_REWIND? */ - { 0x00, KEY_0 }, - { 0x17, KEY_DIGITS }, /*XXX*/ - - /* 0x14 0x10 * - * MENU INFO * - * OSD */ - { 0x14, KEY_MENU }, - { 0x10, KEY_INFO }, - - /* 0x0b * - * Up * - * * - * 0x18 0x16 0x0c * - * Left Ok Right * - * * - * 0x015 * - * Down * - * */ - { 0x0b, KEY_UP }, - { 0x18, KEY_LEFT }, - { 0x16, KEY_OK }, /*XXX KEY_SELECT? KEY_ENTER? */ - { 0x0c, KEY_RIGHT }, - { 0x15, KEY_DOWN }, - - /* 0x11 0x0d * - * TV/AV MODE * - * SOURCE STEREO * - * */ - { 0x11, KEY_TV }, /*XXX*/ - { 0x0d, KEY_MODE }, /*XXX there's no KEY_STEREO */ - - /* 0x0f 0x1b 0x1a * - * AUDIO Vol+ Chan+ * - * TIMESHIFT??? * - * * - * 0x0e 0x1f 0x1e * - * SLEEP Vol- Chan- * - * */ - { 0x0f, KEY_AUDIO }, - { 0x1b, KEY_VOLUMEUP }, - { 0x1a, KEY_CHANNELUP }, - { 0x0e, KEY_TIME }, - { 0x1f, KEY_VOLUMEDOWN }, - { 0x1e, KEY_CHANNELDOWN }, - - /* 0x13 0x19 * - * MUTE SNAPSHOT* - * */ - { 0x13, KEY_MUTE }, - { 0x19, KEY_CAMERA }, - - /* 0x1d unused ? */ -}; - -struct ir_scancode_table ir_codes_manli_table = { - .scan = ir_codes_manli, - .size = ARRAY_SIZE(ir_codes_manli), -}; -EXPORT_SYMBOL_GPL(ir_codes_manli_table); - -/* Mike Baikov <mike@baikov.com> */ -static struct ir_scancode ir_codes_gotview7135[] = { - - { 0x11, KEY_POWER }, - { 0x35, KEY_TV }, - { 0x1b, KEY_0 }, - { 0x29, KEY_1 }, - { 0x19, KEY_2 }, - { 0x39, KEY_3 }, - { 0x1f, KEY_4 }, - { 0x2c, KEY_5 }, - { 0x21, KEY_6 }, - { 0x24, KEY_7 }, - { 0x18, KEY_8 }, - { 0x2b, KEY_9 }, - { 0x3b, KEY_AGAIN }, /* LOOP */ - { 0x06, KEY_AUDIO }, - { 0x31, KEY_PRINT }, /* PREVIEW */ - { 0x3e, KEY_VIDEO }, - { 0x10, KEY_CHANNELUP }, - { 0x20, KEY_CHANNELDOWN }, - { 0x0c, KEY_VOLUMEDOWN }, - { 0x28, KEY_VOLUMEUP }, - { 0x08, KEY_MUTE }, - { 0x26, KEY_SEARCH }, /* SCAN */ - { 0x3f, KEY_CAMERA }, /* SNAPSHOT */ - { 0x12, KEY_RECORD }, - { 0x32, KEY_STOP }, - { 0x3c, KEY_PLAY }, - { 0x1d, KEY_REWIND }, - { 0x2d, KEY_PAUSE }, - { 0x0d, KEY_FORWARD }, - { 0x05, KEY_ZOOM }, /*FULL*/ - - { 0x2a, KEY_F21 }, /* LIVE TIMESHIFT */ - { 0x0e, KEY_F22 }, /* MIN TIMESHIFT */ - { 0x1e, KEY_TIME }, /* TIMESHIFT */ - { 0x38, KEY_F24 }, /* NORMAL TIMESHIFT */ -}; - -struct ir_scancode_table ir_codes_gotview7135_table = { - .scan = ir_codes_gotview7135, - .size = ARRAY_SIZE(ir_codes_gotview7135), -}; -EXPORT_SYMBOL_GPL(ir_codes_gotview7135_table); - -static struct ir_scancode ir_codes_purpletv[] = { - { 0x03, KEY_POWER }, - { 0x6f, KEY_MUTE }, - { 0x10, KEY_BACKSPACE }, /* Recall */ - - { 0x11, KEY_0 }, - { 0x04, KEY_1 }, - { 0x05, KEY_2 }, - { 0x06, KEY_3 }, - { 0x08, KEY_4 }, - { 0x09, KEY_5 }, - { 0x0a, KEY_6 }, - { 0x0c, KEY_7 }, - { 0x0d, KEY_8 }, - { 0x0e, KEY_9 }, - { 0x12, KEY_DOT }, /* 100+ */ - - { 0x07, KEY_VOLUMEUP }, - { 0x0b, KEY_VOLUMEDOWN }, - { 0x1a, KEY_KPPLUS }, - { 0x18, KEY_KPMINUS }, - { 0x15, KEY_UP }, - { 0x1d, KEY_DOWN }, - { 0x0f, KEY_CHANNELUP }, - { 0x13, KEY_CHANNELDOWN }, - { 0x48, KEY_ZOOM }, - - { 0x1b, KEY_VIDEO }, /* Video source */ - { 0x1f, KEY_CAMERA }, /* Snapshot */ - { 0x49, KEY_LANGUAGE }, /* MTS Select */ - { 0x19, KEY_SEARCH }, /* Auto Scan */ - - { 0x4b, KEY_RECORD }, - { 0x46, KEY_PLAY }, - { 0x45, KEY_PAUSE }, /* Pause */ - { 0x44, KEY_STOP }, - { 0x43, KEY_TIME }, /* Time Shift */ - { 0x17, KEY_CHANNEL }, /* SURF CH */ - { 0x40, KEY_FORWARD }, /* Forward ? */ - { 0x42, KEY_REWIND }, /* Backward ? */ - -}; - -struct ir_scancode_table ir_codes_purpletv_table = { - .scan = ir_codes_purpletv, - .size = ARRAY_SIZE(ir_codes_purpletv), -}; -EXPORT_SYMBOL_GPL(ir_codes_purpletv_table); - -/* Mapping for the 28 key remote control as seen at - http://www.sednacomputer.com/photo/cardbus-tv.jpg - Pavel Mihaylov <bin@bash.info> - Also for the remote bundled with Kozumi KTV-01C card */ -static struct ir_scancode ir_codes_pctv_sedna[] = { - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x0a, KEY_AGAIN }, /* Recall */ - { 0x0b, KEY_CHANNELUP }, - { 0x0c, KEY_VOLUMEUP }, - { 0x0d, KEY_MODE }, /* Stereo */ - { 0x0e, KEY_STOP }, - { 0x0f, KEY_PREVIOUSSONG }, - { 0x10, KEY_ZOOM }, - { 0x11, KEY_TUNER }, /* Source */ - { 0x12, KEY_POWER }, - { 0x13, KEY_MUTE }, - { 0x15, KEY_CHANNELDOWN }, - { 0x18, KEY_VOLUMEDOWN }, - { 0x19, KEY_CAMERA }, /* Snapshot */ - { 0x1a, KEY_NEXTSONG }, - { 0x1b, KEY_TIME }, /* Time Shift */ - { 0x1c, KEY_RADIO }, /* FM Radio */ - { 0x1d, KEY_RECORD }, - { 0x1e, KEY_PAUSE }, - /* additional codes for Kozumi's remote */ - { 0x14, KEY_INFO }, /* OSD */ - { 0x16, KEY_OK }, /* OK */ - { 0x17, KEY_DIGITS }, /* Plus */ - { 0x1f, KEY_PLAY }, /* Play */ -}; - -struct ir_scancode_table ir_codes_pctv_sedna_table = { - .scan = ir_codes_pctv_sedna, - .size = ARRAY_SIZE(ir_codes_pctv_sedna), -}; -EXPORT_SYMBOL_GPL(ir_codes_pctv_sedna_table); - -/* Mark Phalan <phalanm@o2.ie> */ -static struct ir_scancode ir_codes_pv951[] = { - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x12, KEY_POWER }, - { 0x10, KEY_MUTE }, - { 0x1f, KEY_VOLUMEDOWN }, - { 0x1b, KEY_VOLUMEUP }, - { 0x1a, KEY_CHANNELUP }, - { 0x1e, KEY_CHANNELDOWN }, - { 0x0e, KEY_PAGEUP }, - { 0x1d, KEY_PAGEDOWN }, - { 0x13, KEY_SOUND }, - - { 0x18, KEY_KPPLUSMINUS }, /* CH +/- */ - { 0x16, KEY_SUBTITLE }, /* CC */ - { 0x0d, KEY_TEXT }, /* TTX */ - { 0x0b, KEY_TV }, /* AIR/CBL */ - { 0x11, KEY_PC }, /* PC/TV */ - { 0x17, KEY_OK }, /* CH RTN */ - { 0x19, KEY_MODE }, /* FUNC */ - { 0x0c, KEY_SEARCH }, /* AUTOSCAN */ - - /* Not sure what to do with these ones! */ - { 0x0f, KEY_SELECT }, /* SOURCE */ - { 0x0a, KEY_KPPLUS }, /* +100 */ - { 0x14, KEY_EQUAL }, /* SYNC */ - { 0x1c, KEY_MEDIA }, /* PC/TV */ -}; - -struct ir_scancode_table ir_codes_pv951_table = { - .scan = ir_codes_pv951, - .size = ARRAY_SIZE(ir_codes_pv951), -}; -EXPORT_SYMBOL_GPL(ir_codes_pv951_table); - -/* generic RC5 keytable */ -/* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */ -/* used by old (black) Hauppauge remotes */ -static struct ir_scancode ir_codes_rc5_tv[] = { - /* Keys 0 to 9 */ - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x0b, KEY_CHANNEL }, /* channel / program (japan: 11) */ - { 0x0c, KEY_POWER }, /* standby */ - { 0x0d, KEY_MUTE }, /* mute / demute */ - { 0x0f, KEY_TV }, /* display */ - { 0x10, KEY_VOLUMEUP }, - { 0x11, KEY_VOLUMEDOWN }, - { 0x12, KEY_BRIGHTNESSUP }, - { 0x13, KEY_BRIGHTNESSDOWN }, - { 0x1e, KEY_SEARCH }, /* search + */ - { 0x20, KEY_CHANNELUP }, /* channel / program + */ - { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x22, KEY_CHANNEL }, /* alt / channel */ - { 0x23, KEY_LANGUAGE }, /* 1st / 2nd language */ - { 0x26, KEY_SLEEP }, /* sleeptimer */ - { 0x2e, KEY_MENU }, /* 2nd controls (USA: menu) */ - { 0x30, KEY_PAUSE }, - { 0x32, KEY_REWIND }, - { 0x33, KEY_GOTO }, - { 0x35, KEY_PLAY }, - { 0x36, KEY_STOP }, - { 0x37, KEY_RECORD }, /* recording */ - { 0x3c, KEY_TEXT }, /* teletext submode (Japan: 12) */ - { 0x3d, KEY_SUSPEND }, /* system standby */ - -}; - -struct ir_scancode_table ir_codes_rc5_tv_table = { - .scan = ir_codes_rc5_tv, - .size = ARRAY_SIZE(ir_codes_rc5_tv), -}; -EXPORT_SYMBOL_GPL(ir_codes_rc5_tv_table); - -/* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */ -static struct ir_scancode ir_codes_winfast[] = { - /* Keys 0 to 9 */ - { 0x12, KEY_0 }, - { 0x05, KEY_1 }, - { 0x06, KEY_2 }, - { 0x07, KEY_3 }, - { 0x09, KEY_4 }, - { 0x0a, KEY_5 }, - { 0x0b, KEY_6 }, - { 0x0d, KEY_7 }, - { 0x0e, KEY_8 }, - { 0x0f, KEY_9 }, - - { 0x00, KEY_POWER }, - { 0x1b, KEY_AUDIO }, /* Audio Source */ - { 0x02, KEY_TUNER }, /* TV/FM, not on Y0400052 */ - { 0x1e, KEY_VIDEO }, /* Video Source */ - { 0x16, KEY_INFO }, /* Display information */ - { 0x04, KEY_VOLUMEUP }, - { 0x08, KEY_VOLUMEDOWN }, - { 0x0c, KEY_CHANNELUP }, - { 0x10, KEY_CHANNELDOWN }, - { 0x03, KEY_ZOOM }, /* fullscreen */ - { 0x1f, KEY_TEXT }, /* closed caption/teletext */ - { 0x20, KEY_SLEEP }, - { 0x29, KEY_CLEAR }, /* boss key */ - { 0x14, KEY_MUTE }, - { 0x2b, KEY_RED }, - { 0x2c, KEY_GREEN }, - { 0x2d, KEY_YELLOW }, - { 0x2e, KEY_BLUE }, - { 0x18, KEY_KPPLUS }, /* fine tune + , not on Y040052 */ - { 0x19, KEY_KPMINUS }, /* fine tune - , not on Y040052 */ - { 0x2a, KEY_MEDIA }, /* PIP (Picture in picture */ - { 0x21, KEY_DOT }, - { 0x13, KEY_ENTER }, - { 0x11, KEY_LAST }, /* Recall (last channel */ - { 0x22, KEY_PREVIOUS }, - { 0x23, KEY_PLAYPAUSE }, - { 0x24, KEY_NEXT }, - { 0x25, KEY_TIME }, /* Time Shifting */ - { 0x26, KEY_STOP }, - { 0x27, KEY_RECORD }, - { 0x28, KEY_SAVE }, /* Screenshot */ - { 0x2f, KEY_MENU }, - { 0x30, KEY_CANCEL }, - { 0x31, KEY_CHANNEL }, /* Channel Surf */ - { 0x32, KEY_SUBTITLE }, - { 0x33, KEY_LANGUAGE }, - { 0x34, KEY_REWIND }, - { 0x35, KEY_FASTFORWARD }, - { 0x36, KEY_TV }, - { 0x37, KEY_RADIO }, /* FM */ - { 0x38, KEY_DVD }, - - { 0x1a, KEY_MODE}, /* change to MCE mode on Y04G0051 */ - { 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */ - { 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */ - { 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */ - { 0x3f, KEY_F24 } /* MCE -CH, on Y04G0033 */ -}; - -struct ir_scancode_table ir_codes_winfast_table = { - .scan = ir_codes_winfast, - .size = ARRAY_SIZE(ir_codes_winfast), -}; -EXPORT_SYMBOL_GPL(ir_codes_winfast_table); - -static struct ir_scancode ir_codes_pinnacle_color[] = { - { 0x59, KEY_MUTE }, - { 0x4a, KEY_POWER }, - - { 0x18, KEY_TEXT }, - { 0x26, KEY_TV }, - { 0x3d, KEY_PRINT }, - - { 0x48, KEY_RED }, - { 0x04, KEY_GREEN }, - { 0x11, KEY_YELLOW }, - { 0x00, KEY_BLUE }, - - { 0x2d, KEY_VOLUMEUP }, - { 0x1e, KEY_VOLUMEDOWN }, - - { 0x49, KEY_MENU }, - - { 0x16, KEY_CHANNELUP }, - { 0x17, KEY_CHANNELDOWN }, - - { 0x20, KEY_UP }, - { 0x21, KEY_DOWN }, - { 0x22, KEY_LEFT }, - { 0x23, KEY_RIGHT }, - { 0x0d, KEY_SELECT }, - - { 0x08, KEY_BACK }, - { 0x07, KEY_REFRESH }, - - { 0x2f, KEY_ZOOM }, - { 0x29, KEY_RECORD }, - - { 0x4b, KEY_PAUSE }, - { 0x4d, KEY_REWIND }, - { 0x2e, KEY_PLAY }, - { 0x4e, KEY_FORWARD }, - { 0x53, KEY_PREVIOUS }, - { 0x4c, KEY_STOP }, - { 0x54, KEY_NEXT }, - - { 0x69, KEY_0 }, - { 0x6a, KEY_1 }, - { 0x6b, KEY_2 }, - { 0x6c, KEY_3 }, - { 0x6d, KEY_4 }, - { 0x6e, KEY_5 }, - { 0x6f, KEY_6 }, - { 0x70, KEY_7 }, - { 0x71, KEY_8 }, - { 0x72, KEY_9 }, - - { 0x74, KEY_CHANNEL }, - { 0x0a, KEY_BACKSPACE }, -}; - -struct ir_scancode_table ir_codes_pinnacle_color_table = { - .scan = ir_codes_pinnacle_color, - .size = ARRAY_SIZE(ir_codes_pinnacle_color), -}; -EXPORT_SYMBOL_GPL(ir_codes_pinnacle_color_table); - -/* Hauppauge: the newer, gray remotes (seems there are multiple - * slightly different versions), shipped with cx88+ivtv cards. - * almost rc5 coding, but some non-standard keys */ -static struct ir_scancode ir_codes_hauppauge_new[] = { - /* Keys 0 to 9 */ - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - { 0x0a, KEY_TEXT }, /* keypad asterisk as well */ - { 0x0b, KEY_RED }, /* red button */ - { 0x0c, KEY_RADIO }, - { 0x0d, KEY_MENU }, - { 0x0e, KEY_SUBTITLE }, /* also the # key */ - { 0x0f, KEY_MUTE }, - { 0x10, KEY_VOLUMEUP }, - { 0x11, KEY_VOLUMEDOWN }, - { 0x12, KEY_PREVIOUS }, /* previous channel */ - { 0x14, KEY_UP }, - { 0x15, KEY_DOWN }, - { 0x16, KEY_LEFT }, - { 0x17, KEY_RIGHT }, - { 0x18, KEY_VIDEO }, /* Videos */ - { 0x19, KEY_AUDIO }, /* Music */ - /* 0x1a: Pictures - presume this means - "Multimedia Home Platform" - - no "PICTURES" key in input.h - */ - { 0x1a, KEY_MHP }, - - { 0x1b, KEY_EPG }, /* Guide */ - { 0x1c, KEY_TV }, - { 0x1e, KEY_NEXTSONG }, /* skip >| */ - { 0x1f, KEY_EXIT }, /* back/exit */ - { 0x20, KEY_CHANNELUP }, /* channel / program + */ - { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x22, KEY_CHANNEL }, /* source (old black remote) */ - { 0x24, KEY_PREVIOUSSONG }, /* replay |< */ - { 0x25, KEY_ENTER }, /* OK */ - { 0x26, KEY_SLEEP }, /* minimize (old black remote) */ - { 0x29, KEY_BLUE }, /* blue key */ - { 0x2e, KEY_GREEN }, /* green button */ - { 0x30, KEY_PAUSE }, /* pause */ - { 0x32, KEY_REWIND }, /* backward << */ - { 0x34, KEY_FASTFORWARD }, /* forward >> */ - { 0x35, KEY_PLAY }, - { 0x36, KEY_STOP }, - { 0x37, KEY_RECORD }, /* recording */ - { 0x38, KEY_YELLOW }, /* yellow key */ - { 0x3b, KEY_SELECT }, /* top right button */ - { 0x3c, KEY_ZOOM }, /* full */ - { 0x3d, KEY_POWER }, /* system power (green button) */ -}; - -struct ir_scancode_table ir_codes_hauppauge_new_table = { - .scan = ir_codes_hauppauge_new, - .size = ARRAY_SIZE(ir_codes_hauppauge_new), -}; -EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table); - -static struct ir_scancode ir_codes_npgtech[] = { - { 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */ - { 0x2a, KEY_FRONT }, - - { 0x3e, KEY_1 }, - { 0x02, KEY_2 }, - { 0x06, KEY_3 }, - { 0x0a, KEY_4 }, - { 0x0e, KEY_5 }, - { 0x12, KEY_6 }, - { 0x16, KEY_7 }, - { 0x1a, KEY_8 }, - { 0x1e, KEY_9 }, - { 0x3a, KEY_0 }, - { 0x22, KEY_NUMLOCK }, /* -/-- */ - { 0x20, KEY_REFRESH }, - - { 0x03, KEY_BRIGHTNESSDOWN }, - { 0x28, KEY_AUDIO }, - { 0x3c, KEY_CHANNELUP }, - { 0x3f, KEY_VOLUMEDOWN }, - { 0x2e, KEY_MUTE }, - { 0x3b, KEY_VOLUMEUP }, - { 0x00, KEY_CHANNELDOWN }, - { 0x07, KEY_BRIGHTNESSUP }, - { 0x2c, KEY_TEXT }, - - { 0x37, KEY_RECORD }, - { 0x17, KEY_PLAY }, - { 0x13, KEY_PAUSE }, - { 0x26, KEY_STOP }, - { 0x18, KEY_FASTFORWARD }, - { 0x14, KEY_REWIND }, - { 0x33, KEY_ZOOM }, - { 0x32, KEY_KEYBOARD }, - { 0x30, KEY_GOTO }, /* Pointing arrow */ - { 0x36, KEY_MACRO }, /* Maximize/Minimize (yellow) */ - { 0x0b, KEY_RADIO }, - { 0x10, KEY_POWER }, - -}; - -struct ir_scancode_table ir_codes_npgtech_table = { - .scan = ir_codes_npgtech, - .size = ARRAY_SIZE(ir_codes_npgtech), -}; -EXPORT_SYMBOL_GPL(ir_codes_npgtech_table); - -/* Norwood Micro (non-Pro) TV Tuner - By Peter Naulls <peter@chocky.org> - Key comments are the functions given in the manual */ -static struct ir_scancode ir_codes_norwood[] = { - /* Keys 0 to 9 */ - { 0x20, KEY_0 }, - { 0x21, KEY_1 }, - { 0x22, KEY_2 }, - { 0x23, KEY_3 }, - { 0x24, KEY_4 }, - { 0x25, KEY_5 }, - { 0x26, KEY_6 }, - { 0x27, KEY_7 }, - { 0x28, KEY_8 }, - { 0x29, KEY_9 }, - - { 0x78, KEY_TUNER }, /* Video Source */ - { 0x2c, KEY_EXIT }, /* Open/Close software */ - { 0x2a, KEY_SELECT }, /* 2 Digit Select */ - { 0x69, KEY_AGAIN }, /* Recall */ - - { 0x32, KEY_BRIGHTNESSUP }, /* Brightness increase */ - { 0x33, KEY_BRIGHTNESSDOWN }, /* Brightness decrease */ - { 0x6b, KEY_KPPLUS }, /* (not named >>>>>) */ - { 0x6c, KEY_KPMINUS }, /* (not named <<<<<) */ - - { 0x2d, KEY_MUTE }, /* Mute */ - { 0x30, KEY_VOLUMEUP }, /* Volume up */ - { 0x31, KEY_VOLUMEDOWN }, /* Volume down */ - { 0x60, KEY_CHANNELUP }, /* Channel up */ - { 0x61, KEY_CHANNELDOWN }, /* Channel down */ - - { 0x3f, KEY_RECORD }, /* Record */ - { 0x37, KEY_PLAY }, /* Play */ - { 0x36, KEY_PAUSE }, /* Pause */ - { 0x2b, KEY_STOP }, /* Stop */ - { 0x67, KEY_FASTFORWARD }, /* Foward */ - { 0x66, KEY_REWIND }, /* Rewind */ - { 0x3e, KEY_SEARCH }, /* Auto Scan */ - { 0x2e, KEY_CAMERA }, /* Capture Video */ - { 0x6d, KEY_MENU }, /* Show/Hide Control */ - { 0x2f, KEY_ZOOM }, /* Full Screen */ - { 0x34, KEY_RADIO }, /* FM */ - { 0x65, KEY_POWER }, /* Computer power */ -}; - -struct ir_scancode_table ir_codes_norwood_table = { - .scan = ir_codes_norwood, - .size = ARRAY_SIZE(ir_codes_norwood), -}; -EXPORT_SYMBOL_GPL(ir_codes_norwood_table); - -/* From reading the following remotes: - * Zenith Universal 7 / TV Mode 807 / VCR Mode 837 - * Hauppauge (from NOVA-CI-s box product) - * This is a "middle of the road" approach, differences are noted - */ -static struct ir_scancode ir_codes_budget_ci_old[] = { - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - { 0x0a, KEY_ENTER }, - { 0x0b, KEY_RED }, - { 0x0c, KEY_POWER }, /* RADIO on Hauppauge */ - { 0x0d, KEY_MUTE }, - { 0x0f, KEY_A }, /* TV on Hauppauge */ - { 0x10, KEY_VOLUMEUP }, - { 0x11, KEY_VOLUMEDOWN }, - { 0x14, KEY_B }, - { 0x1c, KEY_UP }, - { 0x1d, KEY_DOWN }, - { 0x1e, KEY_OPTION }, /* RESERVED on Hauppauge */ - { 0x1f, KEY_BREAK }, - { 0x20, KEY_CHANNELUP }, - { 0x21, KEY_CHANNELDOWN }, - { 0x22, KEY_PREVIOUS }, /* Prev Ch on Zenith, SOURCE on Hauppauge */ - { 0x24, KEY_RESTART }, - { 0x25, KEY_OK }, - { 0x26, KEY_CYCLEWINDOWS }, /* MINIMIZE on Hauppauge */ - { 0x28, KEY_ENTER }, /* VCR mode on Zenith */ - { 0x29, KEY_PAUSE }, - { 0x2b, KEY_RIGHT }, - { 0x2c, KEY_LEFT }, - { 0x2e, KEY_MENU }, /* FULL SCREEN on Hauppauge */ - { 0x30, KEY_SLOW }, - { 0x31, KEY_PREVIOUS }, /* VCR mode on Zenith */ - { 0x32, KEY_REWIND }, - { 0x34, KEY_FASTFORWARD }, - { 0x35, KEY_PLAY }, - { 0x36, KEY_STOP }, - { 0x37, KEY_RECORD }, - { 0x38, KEY_TUNER }, /* TV/VCR on Zenith */ - { 0x3a, KEY_C }, - { 0x3c, KEY_EXIT }, - { 0x3d, KEY_POWER2 }, - { 0x3e, KEY_TUNER }, -}; - -struct ir_scancode_table ir_codes_budget_ci_old_table = { - .scan = ir_codes_budget_ci_old, - .size = ARRAY_SIZE(ir_codes_budget_ci_old), -}; -EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old_table); - -/* - * Marc Fargas <telenieko@telenieko.com> - * this is the remote control that comes with the asus p7131 - * which has a label saying is "Model PC-39" - */ -static struct ir_scancode ir_codes_asus_pc39[] = { - /* Keys 0 to 9 */ - { 0x15, KEY_0 }, - { 0x29, KEY_1 }, - { 0x2d, KEY_2 }, - { 0x2b, KEY_3 }, - { 0x09, KEY_4 }, - { 0x0d, KEY_5 }, - { 0x0b, KEY_6 }, - { 0x31, KEY_7 }, - { 0x35, KEY_8 }, - { 0x33, KEY_9 }, - - { 0x3e, KEY_RADIO }, /* radio */ - { 0x03, KEY_MENU }, /* dvd/menu */ - { 0x2a, KEY_VOLUMEUP }, - { 0x19, KEY_VOLUMEDOWN }, - { 0x37, KEY_UP }, - { 0x3b, KEY_DOWN }, - { 0x27, KEY_LEFT }, - { 0x2f, KEY_RIGHT }, - { 0x25, KEY_VIDEO }, /* video */ - { 0x39, KEY_AUDIO }, /* music */ - - { 0x21, KEY_TV }, /* tv */ - { 0x1d, KEY_EXIT }, /* back */ - { 0x0a, KEY_CHANNELUP }, /* channel / program + */ - { 0x1b, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x1a, KEY_ENTER }, /* enter */ - - { 0x06, KEY_PAUSE }, /* play/pause */ - { 0x1e, KEY_PREVIOUS }, /* rew */ - { 0x26, KEY_NEXT }, /* forward */ - { 0x0e, KEY_REWIND }, /* backward << */ - { 0x3a, KEY_FASTFORWARD }, /* forward >> */ - { 0x36, KEY_STOP }, - { 0x2e, KEY_RECORD }, /* recording */ - { 0x16, KEY_POWER }, /* the button that reads "close" */ - - { 0x11, KEY_ZOOM }, /* full screen */ - { 0x13, KEY_MACRO }, /* recall */ - { 0x23, KEY_HOME }, /* home */ - { 0x05, KEY_PVR }, /* picture */ - { 0x3d, KEY_MUTE }, /* mute */ - { 0x01, KEY_DVD }, /* dvd */ -}; - -struct ir_scancode_table ir_codes_asus_pc39_table = { - .scan = ir_codes_asus_pc39, - .size = ARRAY_SIZE(ir_codes_asus_pc39), -}; -EXPORT_SYMBOL_GPL(ir_codes_asus_pc39_table); - - -/* Encore ENLTV-FM - black plastic, white front cover with white glowing buttons - Juan Pablo Sormani <sorman@gmail.com> */ -static struct ir_scancode ir_codes_encore_enltv[] = { - - /* Power button does nothing, neither in Windows app, - although it sends data (used for BIOS wakeup?) */ - { 0x0d, KEY_MUTE }, - - { 0x1e, KEY_TV }, - { 0x00, KEY_VIDEO }, - { 0x01, KEY_AUDIO }, /* music */ - { 0x02, KEY_MHP }, /* picture */ - - { 0x1f, KEY_1 }, - { 0x03, KEY_2 }, - { 0x04, KEY_3 }, - { 0x05, KEY_4 }, - { 0x1c, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x1d, KEY_9 }, - { 0x0a, KEY_0 }, - - { 0x09, KEY_LIST }, /* -/-- */ - { 0x0b, KEY_LAST }, /* recall */ - - { 0x14, KEY_HOME }, /* win start menu */ - { 0x15, KEY_EXIT }, /* exit */ - { 0x16, KEY_CHANNELUP }, /* UP */ - { 0x12, KEY_CHANNELDOWN }, /* DOWN */ - { 0x0c, KEY_VOLUMEUP }, /* RIGHT */ - { 0x17, KEY_VOLUMEDOWN }, /* LEFT */ - - { 0x18, KEY_ENTER }, /* OK */ - - { 0x0e, KEY_ESC }, - { 0x13, KEY_CYCLEWINDOWS }, /* desktop */ - { 0x11, KEY_TAB }, - { 0x19, KEY_SWITCHVIDEOMODE }, /* switch */ - - { 0x1a, KEY_MENU }, - { 0x1b, KEY_ZOOM }, /* fullscreen */ - { 0x44, KEY_TIME }, /* time shift */ - { 0x40, KEY_MODE }, /* source */ - - { 0x5a, KEY_RECORD }, - { 0x42, KEY_PLAY }, /* play/pause */ - { 0x45, KEY_STOP }, - { 0x43, KEY_CAMERA }, /* camera icon */ - - { 0x48, KEY_REWIND }, - { 0x4a, KEY_FASTFORWARD }, - { 0x49, KEY_PREVIOUS }, - { 0x4b, KEY_NEXT }, - - { 0x4c, KEY_FAVORITES }, /* tv wall */ - { 0x4d, KEY_SOUND }, /* DVD sound */ - { 0x4e, KEY_LANGUAGE }, /* DVD lang */ - { 0x4f, KEY_TEXT }, /* DVD text */ - - { 0x50, KEY_SLEEP }, /* shutdown */ - { 0x51, KEY_MODE }, /* stereo > main */ - { 0x52, KEY_SELECT }, /* stereo > sap */ - { 0x53, KEY_PROG1 }, /* teletext */ - - - { 0x59, KEY_RED }, /* AP1 */ - { 0x41, KEY_GREEN }, /* AP2 */ - { 0x47, KEY_YELLOW }, /* AP3 */ - { 0x57, KEY_BLUE }, /* AP4 */ -}; - -struct ir_scancode_table ir_codes_encore_enltv_table = { - .scan = ir_codes_encore_enltv, - .size = ARRAY_SIZE(ir_codes_encore_enltv), -}; -EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_table); - -/* Encore ENLTV2-FM - silver plastic - "Wand Media" written at the botton - Mauro Carvalho Chehab <mchehab@infradead.org> */ -static struct ir_scancode ir_codes_encore_enltv2[] = { - { 0x4c, KEY_POWER2 }, - { 0x4a, KEY_TUNER }, - { 0x40, KEY_1 }, - { 0x60, KEY_2 }, - { 0x50, KEY_3 }, - { 0x70, KEY_4 }, - { 0x48, KEY_5 }, - { 0x68, KEY_6 }, - { 0x58, KEY_7 }, - { 0x78, KEY_8 }, - { 0x44, KEY_9 }, - { 0x54, KEY_0 }, - - { 0x64, KEY_LAST }, /* +100 */ - { 0x4e, KEY_AGAIN }, /* Recall */ - - { 0x6c, KEY_SWITCHVIDEOMODE }, /* Video Source */ - { 0x5e, KEY_MENU }, - { 0x56, KEY_SCREEN }, - { 0x7a, KEY_SETUP }, - - { 0x46, KEY_MUTE }, - { 0x5c, KEY_MODE }, /* Stereo */ - { 0x74, KEY_INFO }, - { 0x7c, KEY_CLEAR }, - - { 0x55, KEY_UP }, - { 0x49, KEY_DOWN }, - { 0x7e, KEY_LEFT }, - { 0x59, KEY_RIGHT }, - { 0x6a, KEY_ENTER }, - - { 0x42, KEY_VOLUMEUP }, - { 0x62, KEY_VOLUMEDOWN }, - { 0x52, KEY_CHANNELUP }, - { 0x72, KEY_CHANNELDOWN }, - - { 0x41, KEY_RECORD }, - { 0x51, KEY_CAMERA }, /* Snapshot */ - { 0x75, KEY_TIME }, /* Timeshift */ - { 0x71, KEY_TV2 }, /* PIP */ - - { 0x45, KEY_REWIND }, - { 0x6f, KEY_PAUSE }, - { 0x7d, KEY_FORWARD }, - { 0x79, KEY_STOP }, -}; - -struct ir_scancode_table ir_codes_encore_enltv2_table = { - .scan = ir_codes_encore_enltv2, - .size = ARRAY_SIZE(ir_codes_encore_enltv2), -}; -EXPORT_SYMBOL_GPL(ir_codes_encore_enltv2_table); - -/* for the Technotrend 1500 bundled remotes (grey and black): */ -static struct ir_scancode ir_codes_tt_1500[] = { - { 0x01, KEY_POWER }, - { 0x02, KEY_SHUFFLE }, /* ? double-arrow key */ - { 0x03, KEY_1 }, - { 0x04, KEY_2 }, - { 0x05, KEY_3 }, - { 0x06, KEY_4 }, - { 0x07, KEY_5 }, - { 0x08, KEY_6 }, - { 0x09, KEY_7 }, - { 0x0a, KEY_8 }, - { 0x0b, KEY_9 }, - { 0x0c, KEY_0 }, - { 0x0d, KEY_UP }, - { 0x0e, KEY_LEFT }, - { 0x0f, KEY_OK }, - { 0x10, KEY_RIGHT }, - { 0x11, KEY_DOWN }, - { 0x12, KEY_INFO }, - { 0x13, KEY_EXIT }, - { 0x14, KEY_RED }, - { 0x15, KEY_GREEN }, - { 0x16, KEY_YELLOW }, - { 0x17, KEY_BLUE }, - { 0x18, KEY_MUTE }, - { 0x19, KEY_TEXT }, - { 0x1a, KEY_MODE }, /* ? TV/Radio */ - { 0x21, KEY_OPTION }, - { 0x22, KEY_EPG }, - { 0x23, KEY_CHANNELUP }, - { 0x24, KEY_CHANNELDOWN }, - { 0x25, KEY_VOLUMEUP }, - { 0x26, KEY_VOLUMEDOWN }, - { 0x27, KEY_SETUP }, - { 0x3a, KEY_RECORD }, /* these keys are only in the black remote */ - { 0x3b, KEY_PLAY }, - { 0x3c, KEY_STOP }, - { 0x3d, KEY_REWIND }, - { 0x3e, KEY_PAUSE }, - { 0x3f, KEY_FORWARD }, -}; - -struct ir_scancode_table ir_codes_tt_1500_table = { - .scan = ir_codes_tt_1500, - .size = ARRAY_SIZE(ir_codes_tt_1500), -}; -EXPORT_SYMBOL_GPL(ir_codes_tt_1500_table); - -/* DViCO FUSION HDTV MCE remote */ -static struct ir_scancode ir_codes_fusionhdtv_mce[] = { - - { 0x0b, KEY_1 }, - { 0x17, KEY_2 }, - { 0x1b, KEY_3 }, - { 0x07, KEY_4 }, - { 0x50, KEY_5 }, - { 0x54, KEY_6 }, - { 0x48, KEY_7 }, - { 0x4c, KEY_8 }, - { 0x58, KEY_9 }, - { 0x03, KEY_0 }, - - { 0x5e, KEY_OK }, - { 0x51, KEY_UP }, - { 0x53, KEY_DOWN }, - { 0x5b, KEY_LEFT }, - { 0x5f, KEY_RIGHT }, - - { 0x02, KEY_TV }, /* Labeled DTV on remote */ - { 0x0e, KEY_MP3 }, - { 0x1a, KEY_DVD }, - { 0x1e, KEY_FAVORITES }, /* Labeled CPF on remote */ - { 0x16, KEY_SETUP }, - { 0x46, KEY_POWER2 }, /* TV On/Off button on remote */ - { 0x0a, KEY_EPG }, /* Labeled Guide on remote */ - - { 0x49, KEY_BACK }, - { 0x59, KEY_INFO }, /* Labeled MORE on remote */ - { 0x4d, KEY_MENU }, /* Labeled DVDMENU on remote */ - { 0x55, KEY_CYCLEWINDOWS }, /* Labeled ALT-TAB on remote */ - - { 0x0f, KEY_PREVIOUSSONG }, /* Labeled |<< REPLAY on remote */ - { 0x12, KEY_NEXTSONG }, /* Labeled >>| SKIP on remote */ - { 0x42, KEY_ENTER }, /* Labeled START with a green - MS windows logo on remote */ - - { 0x15, KEY_VOLUMEUP }, - { 0x05, KEY_VOLUMEDOWN }, - { 0x11, KEY_CHANNELUP }, - { 0x09, KEY_CHANNELDOWN }, - - { 0x52, KEY_CAMERA }, - { 0x5a, KEY_TUNER }, - { 0x19, KEY_OPEN }, - - { 0x13, KEY_MODE }, /* 4:3 16:9 select */ - { 0x1f, KEY_ZOOM }, - - { 0x43, KEY_REWIND }, - { 0x47, KEY_PLAYPAUSE }, - { 0x4f, KEY_FASTFORWARD }, - { 0x57, KEY_MUTE }, - { 0x0d, KEY_STOP }, - { 0x01, KEY_RECORD }, - { 0x4e, KEY_POWER }, -}; - -struct ir_scancode_table ir_codes_fusionhdtv_mce_table = { - .scan = ir_codes_fusionhdtv_mce, - .size = ARRAY_SIZE(ir_codes_fusionhdtv_mce), -}; -EXPORT_SYMBOL_GPL(ir_codes_fusionhdtv_mce_table); - -/* Pinnacle PCTV HD 800i mini remote */ -static struct ir_scancode ir_codes_pinnacle_pctv_hd[] = { - - { 0x0f, KEY_1 }, - { 0x15, KEY_2 }, - { 0x10, KEY_3 }, - { 0x18, KEY_4 }, - { 0x1b, KEY_5 }, - { 0x1e, KEY_6 }, - { 0x11, KEY_7 }, - { 0x21, KEY_8 }, - { 0x12, KEY_9 }, - { 0x27, KEY_0 }, - - { 0x24, KEY_ZOOM }, - { 0x2a, KEY_SUBTITLE }, - - { 0x00, KEY_MUTE }, - { 0x01, KEY_ENTER }, /* Pinnacle Logo */ - { 0x39, KEY_POWER }, - - { 0x03, KEY_VOLUMEUP }, - { 0x09, KEY_VOLUMEDOWN }, - { 0x06, KEY_CHANNELUP }, - { 0x0c, KEY_CHANNELDOWN }, - - { 0x2d, KEY_REWIND }, - { 0x30, KEY_PLAYPAUSE }, - { 0x33, KEY_FASTFORWARD }, - { 0x3c, KEY_STOP }, - { 0x36, KEY_RECORD }, - { 0x3f, KEY_EPG }, /* Labeled "?" */ -}; - -struct ir_scancode_table ir_codes_pinnacle_pctv_hd_table = { - .scan = ir_codes_pinnacle_pctv_hd, - .size = ARRAY_SIZE(ir_codes_pinnacle_pctv_hd), -}; -EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd_table); - -/* - * Igor Kuznetsov <igk72@ya.ru> - * Andrey J. Melnikov <temnota@kmv.ru> - * - * Keytable is used by BeholdTV 60x series, M6 series at - * least, and probably other cards too. - * The "ascii-art picture" below (in comments, first row - * is the keycode in hex, and subsequent row(s) shows - * the button labels (several variants when appropriate) - * helps to descide which keycodes to assign to the buttons. - */ -static struct ir_scancode ir_codes_behold[] = { - - /* 0x1c 0x12 * - * TV/FM POWER * - * */ - { 0x1c, KEY_TUNER }, /* XXX KEY_TV / KEY_RADIO */ - { 0x12, KEY_POWER }, - - /* 0x01 0x02 0x03 * - * 1 2 3 * - * * - * 0x04 0x05 0x06 * - * 4 5 6 * - * * - * 0x07 0x08 0x09 * - * 7 8 9 * - * */ - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - - /* 0x0a 0x00 0x17 * - * RECALL 0 MODE * - * */ - { 0x0a, KEY_AGAIN }, - { 0x00, KEY_0 }, - { 0x17, KEY_MODE }, - - /* 0x14 0x10 * - * ASPECT FULLSCREEN * - * */ - { 0x14, KEY_SCREEN }, - { 0x10, KEY_ZOOM }, - - /* 0x0b * - * Up * - * * - * 0x18 0x16 0x0c * - * Left Ok Right * - * * - * 0x015 * - * Down * - * */ - { 0x0b, KEY_CHANNELUP }, - { 0x18, KEY_VOLUMEDOWN }, - { 0x16, KEY_OK }, /* XXX KEY_ENTER */ - { 0x0c, KEY_VOLUMEUP }, - { 0x15, KEY_CHANNELDOWN }, - - /* 0x11 0x0d * - * MUTE INFO * - * */ - { 0x11, KEY_MUTE }, - { 0x0d, KEY_INFO }, - - /* 0x0f 0x1b 0x1a * - * RECORD PLAY/PAUSE STOP * - * * - * 0x0e 0x1f 0x1e * - *TELETEXT AUDIO SOURCE * - * RED YELLOW * - * */ - { 0x0f, KEY_RECORD }, - { 0x1b, KEY_PLAYPAUSE }, - { 0x1a, KEY_STOP }, - { 0x0e, KEY_TEXT }, - { 0x1f, KEY_RED }, /*XXX KEY_AUDIO */ - { 0x1e, KEY_YELLOW }, /*XXX KEY_SOURCE */ - - /* 0x1d 0x13 0x19 * - * SLEEP PREVIEW DVB * - * GREEN BLUE * - * */ - { 0x1d, KEY_SLEEP }, - { 0x13, KEY_GREEN }, - { 0x19, KEY_BLUE }, /* XXX KEY_SAT */ - - /* 0x58 0x5c * - * FREEZE SNAPSHOT * - * */ - { 0x58, KEY_SLOW }, - { 0x5c, KEY_CAMERA }, - -}; - -struct ir_scancode_table ir_codes_behold_table = { - .scan = ir_codes_behold, - .size = ARRAY_SIZE(ir_codes_behold), -}; -EXPORT_SYMBOL_GPL(ir_codes_behold_table); - -/* Beholder Intl. Ltd. 2008 - * Dmitry Belimov d.belimov@google.com - * Keytable is used by BeholdTV Columbus - * The "ascii-art picture" below (in comments, first row - * is the keycode in hex, and subsequent row(s) shows - * the button labels (several variants when appropriate) - * helps to descide which keycodes to assign to the buttons. - */ -static struct ir_scancode ir_codes_behold_columbus[] = { - - /* 0x13 0x11 0x1C 0x12 * - * Mute Source TV/FM Power * - * */ - - { 0x13, KEY_MUTE }, - { 0x11, KEY_PROPS }, - { 0x1C, KEY_TUNER }, /* KEY_TV/KEY_RADIO */ - { 0x12, KEY_POWER }, - - /* 0x01 0x02 0x03 0x0D * - * 1 2 3 Stereo * - * * - * 0x04 0x05 0x06 0x19 * - * 4 5 6 Snapshot * - * * - * 0x07 0x08 0x09 0x10 * - * 7 8 9 Zoom * - * */ - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x0D, KEY_SETUP }, /* Setup key */ - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x19, KEY_CAMERA }, /* Snapshot key */ - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - { 0x10, KEY_ZOOM }, - - /* 0x0A 0x00 0x0B 0x0C * - * RECALL 0 ChannelUp VolumeUp * - * */ - { 0x0A, KEY_AGAIN }, - { 0x00, KEY_0 }, - { 0x0B, KEY_CHANNELUP }, - { 0x0C, KEY_VOLUMEUP }, - - /* 0x1B 0x1D 0x15 0x18 * - * Timeshift Record ChannelDown VolumeDown * - * */ - - { 0x1B, KEY_TIME }, - { 0x1D, KEY_RECORD }, - { 0x15, KEY_CHANNELDOWN }, - { 0x18, KEY_VOLUMEDOWN }, - - /* 0x0E 0x1E 0x0F 0x1A * - * Stop Pause Previouse Next * - * */ - - { 0x0E, KEY_STOP }, - { 0x1E, KEY_PAUSE }, - { 0x0F, KEY_PREVIOUS }, - { 0x1A, KEY_NEXT }, - -}; - -struct ir_scancode_table ir_codes_behold_columbus_table = { - .scan = ir_codes_behold_columbus, - .size = ARRAY_SIZE(ir_codes_behold_columbus), -}; -EXPORT_SYMBOL_GPL(ir_codes_behold_columbus_table); - -/* - * Remote control for the Genius TVGO A11MCE - * Adrian Pardini <pardo.bsso@gmail.com> - */ -static struct ir_scancode ir_codes_genius_tvgo_a11mce[] = { - /* Keys 0 to 9 */ - { 0x48, KEY_0 }, - { 0x09, KEY_1 }, - { 0x1d, KEY_2 }, - { 0x1f, KEY_3 }, - { 0x19, KEY_4 }, - { 0x1b, KEY_5 }, - { 0x11, KEY_6 }, - { 0x17, KEY_7 }, - { 0x12, KEY_8 }, - { 0x16, KEY_9 }, - - { 0x54, KEY_RECORD }, /* recording */ - { 0x06, KEY_MUTE }, /* mute */ - { 0x10, KEY_POWER }, - { 0x40, KEY_LAST }, /* recall */ - { 0x4c, KEY_CHANNELUP }, /* channel / program + */ - { 0x00, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x0d, KEY_VOLUMEUP }, - { 0x15, KEY_VOLUMEDOWN }, - { 0x4d, KEY_OK }, /* also labeled as Pause */ - { 0x1c, KEY_ZOOM }, /* full screen and Stop*/ - { 0x02, KEY_MODE }, /* AV Source or Rewind*/ - { 0x04, KEY_LIST }, /* -/-- */ - /* small arrows above numbers */ - { 0x1a, KEY_NEXT }, /* also Fast Forward */ - { 0x0e, KEY_PREVIOUS }, /* also Rewind */ - /* these are in a rather non standard layout and have - an alternate name written */ - { 0x1e, KEY_UP }, /* Video Setting */ - { 0x0a, KEY_DOWN }, /* Video Default */ - { 0x05, KEY_CAMERA }, /* Snapshot */ - { 0x0c, KEY_RIGHT }, /* Hide Panel */ - /* Four buttons without label */ - { 0x49, KEY_RED }, - { 0x0b, KEY_GREEN }, - { 0x13, KEY_YELLOW }, - { 0x50, KEY_BLUE }, -}; - -struct ir_scancode_table ir_codes_genius_tvgo_a11mce_table = { - .scan = ir_codes_genius_tvgo_a11mce, - .size = ARRAY_SIZE(ir_codes_genius_tvgo_a11mce), -}; -EXPORT_SYMBOL_GPL(ir_codes_genius_tvgo_a11mce_table); - -/* - * Remote control for Powercolor Real Angel 330 - * Daniel Fraga <fragabr@gmail.com> - */ -static struct ir_scancode ir_codes_powercolor_real_angel[] = { - { 0x38, KEY_SWITCHVIDEOMODE }, /* switch inputs */ - { 0x0c, KEY_MEDIA }, /* Turn ON/OFF App */ - { 0x00, KEY_0 }, - { 0x01, KEY_1 }, - { 0x02, KEY_2 }, - { 0x03, KEY_3 }, - { 0x04, KEY_4 }, - { 0x05, KEY_5 }, - { 0x06, KEY_6 }, - { 0x07, KEY_7 }, - { 0x08, KEY_8 }, - { 0x09, KEY_9 }, - { 0x0a, KEY_DIGITS }, /* single, double, tripple digit */ - { 0x29, KEY_PREVIOUS }, /* previous channel */ - { 0x12, KEY_BRIGHTNESSUP }, - { 0x13, KEY_BRIGHTNESSDOWN }, - { 0x2b, KEY_MODE }, /* stereo/mono */ - { 0x2c, KEY_TEXT }, /* teletext */ - { 0x20, KEY_CHANNELUP }, /* channel up */ - { 0x21, KEY_CHANNELDOWN }, /* channel down */ - { 0x10, KEY_VOLUMEUP }, /* volume up */ - { 0x11, KEY_VOLUMEDOWN }, /* volume down */ - { 0x0d, KEY_MUTE }, - { 0x1f, KEY_RECORD }, - { 0x17, KEY_PLAY }, - { 0x16, KEY_PAUSE }, - { 0x0b, KEY_STOP }, - { 0x27, KEY_FASTFORWARD }, - { 0x26, KEY_REWIND }, - { 0x1e, KEY_SEARCH }, /* autoscan */ - { 0x0e, KEY_CAMERA }, /* snapshot */ - { 0x2d, KEY_SETUP }, - { 0x0f, KEY_SCREEN }, /* full screen */ - { 0x14, KEY_RADIO }, /* FM radio */ - { 0x25, KEY_POWER }, /* power */ -}; - -struct ir_scancode_table ir_codes_powercolor_real_angel_table = { - .scan = ir_codes_powercolor_real_angel, - .size = ARRAY_SIZE(ir_codes_powercolor_real_angel), -}; -EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel_table); - -/* Kworld Plus TV Analog Lite PCI IR - Mauro Carvalho Chehab <mchehab@infradead.org> - */ -static struct ir_scancode ir_codes_kworld_plus_tv_analog[] = { - { 0x0c, KEY_PROG1 }, /* Kworld key */ - { 0x16, KEY_CLOSECD }, /* -> ) */ - { 0x1d, KEY_POWER2 }, - - { 0x00, KEY_1 }, - { 0x01, KEY_2 }, - { 0x02, KEY_3 }, /* Two keys have the same code: 3 and left */ - { 0x03, KEY_4 }, /* Two keys have the same code: 3 and right */ - { 0x04, KEY_5 }, - { 0x05, KEY_6 }, - { 0x06, KEY_7 }, - { 0x07, KEY_8 }, - { 0x08, KEY_9 }, - { 0x0a, KEY_0 }, - - { 0x09, KEY_AGAIN }, - { 0x14, KEY_MUTE }, - - { 0x20, KEY_UP }, - { 0x21, KEY_DOWN }, - { 0x0b, KEY_ENTER }, - - { 0x10, KEY_CHANNELUP }, - { 0x11, KEY_CHANNELDOWN }, - - /* Couldn't map key left/key right since those - conflict with '3' and '4' scancodes - I dunno what the original driver does - */ - - { 0x13, KEY_VOLUMEUP }, - { 0x12, KEY_VOLUMEDOWN }, - - /* The lower part of the IR - There are several duplicated keycodes there. - Most of them conflict with digits. - Add mappings just to the unused scancodes. - Somehow, the original driver has a way to know, - but this doesn't seem to be on some GPIO. - Also, it is not related to the time between keyup - and keydown. - */ - { 0x19, KEY_TIME}, /* Timeshift */ - { 0x1a, KEY_STOP}, - { 0x1b, KEY_RECORD}, - - { 0x22, KEY_TEXT}, - - { 0x15, KEY_AUDIO}, /* ((*)) */ - { 0x0f, KEY_ZOOM}, - { 0x1c, KEY_CAMERA}, /* snapshot */ - - { 0x18, KEY_RED}, /* B */ - { 0x23, KEY_GREEN}, /* C */ -}; -struct ir_scancode_table ir_codes_kworld_plus_tv_analog_table = { - .scan = ir_codes_kworld_plus_tv_analog, - .size = ARRAY_SIZE(ir_codes_kworld_plus_tv_analog), -}; -EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog_table); - -/* Kaiomy TVnPC U2 - Mauro Carvalho Chehab <mchehab@infradead.org> - */ -static struct ir_scancode ir_codes_kaiomy[] = { - { 0x43, KEY_POWER2}, - { 0x01, KEY_LIST}, - { 0x0b, KEY_ZOOM}, - { 0x03, KEY_POWER}, - - { 0x04, KEY_1}, - { 0x08, KEY_2}, - { 0x02, KEY_3}, - - { 0x0f, KEY_4}, - { 0x05, KEY_5}, - { 0x06, KEY_6}, - - { 0x0c, KEY_7}, - { 0x0d, KEY_8}, - { 0x0a, KEY_9}, - - { 0x11, KEY_0}, - - { 0x09, KEY_CHANNELUP}, - { 0x07, KEY_CHANNELDOWN}, - - { 0x0e, KEY_VOLUMEUP}, - { 0x13, KEY_VOLUMEDOWN}, - - { 0x10, KEY_HOME}, - { 0x12, KEY_ENTER}, - - { 0x14, KEY_RECORD}, - { 0x15, KEY_STOP}, - { 0x16, KEY_PLAY}, - { 0x17, KEY_MUTE}, - - { 0x18, KEY_UP}, - { 0x19, KEY_DOWN}, - { 0x1a, KEY_LEFT}, - { 0x1b, KEY_RIGHT}, - - { 0x1c, KEY_RED}, - { 0x1d, KEY_GREEN}, - { 0x1e, KEY_YELLOW}, - { 0x1f, KEY_BLUE}, -}; -struct ir_scancode_table ir_codes_kaiomy_table = { - .scan = ir_codes_kaiomy, - .size = ARRAY_SIZE(ir_codes_kaiomy), -}; -EXPORT_SYMBOL_GPL(ir_codes_kaiomy_table); - -static struct ir_scancode ir_codes_avermedia_a16d[] = { - { 0x20, KEY_LIST}, - { 0x00, KEY_POWER}, - { 0x28, KEY_1}, - { 0x18, KEY_2}, - { 0x38, KEY_3}, - { 0x24, KEY_4}, - { 0x14, KEY_5}, - { 0x34, KEY_6}, - { 0x2c, KEY_7}, - { 0x1c, KEY_8}, - { 0x3c, KEY_9}, - { 0x12, KEY_SUBTITLE}, - { 0x22, KEY_0}, - { 0x32, KEY_REWIND}, - { 0x3a, KEY_SHUFFLE}, - { 0x02, KEY_PRINT}, - { 0x11, KEY_CHANNELDOWN}, - { 0x31, KEY_CHANNELUP}, - { 0x0c, KEY_ZOOM}, - { 0x1e, KEY_VOLUMEDOWN}, - { 0x3e, KEY_VOLUMEUP}, - { 0x0a, KEY_MUTE}, - { 0x04, KEY_AUDIO}, - { 0x26, KEY_RECORD}, - { 0x06, KEY_PLAY}, - { 0x36, KEY_STOP}, - { 0x16, KEY_PAUSE}, - { 0x2e, KEY_REWIND}, - { 0x0e, KEY_FASTFORWARD}, - { 0x30, KEY_TEXT}, - { 0x21, KEY_GREEN}, - { 0x01, KEY_BLUE}, - { 0x08, KEY_EPG}, - { 0x2a, KEY_MENU}, -}; -struct ir_scancode_table ir_codes_avermedia_a16d_table = { - .scan = ir_codes_avermedia_a16d, - .size = ARRAY_SIZE(ir_codes_avermedia_a16d), -}; -EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d_table); - -/* Encore ENLTV-FM v5.3 - Mauro Carvalho Chehab <mchehab@infradead.org> - */ -static struct ir_scancode ir_codes_encore_enltv_fm53[] = { - { 0x10, KEY_POWER2}, - { 0x06, KEY_MUTE}, - - { 0x09, KEY_1}, - { 0x1d, KEY_2}, - { 0x1f, KEY_3}, - { 0x19, KEY_4}, - { 0x1b, KEY_5}, - { 0x11, KEY_6}, - { 0x17, KEY_7}, - { 0x12, KEY_8}, - { 0x16, KEY_9}, - { 0x48, KEY_0}, - - { 0x04, KEY_LIST}, /* -/-- */ - { 0x40, KEY_LAST}, /* recall */ - - { 0x02, KEY_MODE}, /* TV/AV */ - { 0x05, KEY_CAMERA}, /* SNAPSHOT */ - - { 0x4c, KEY_CHANNELUP}, /* UP */ - { 0x00, KEY_CHANNELDOWN}, /* DOWN */ - { 0x0d, KEY_VOLUMEUP}, /* RIGHT */ - { 0x15, KEY_VOLUMEDOWN}, /* LEFT */ - { 0x49, KEY_ENTER}, /* OK */ - - { 0x54, KEY_RECORD}, - { 0x4d, KEY_PLAY}, /* pause */ - - { 0x1e, KEY_MENU}, /* video setting */ - { 0x0e, KEY_RIGHT}, /* <- */ - { 0x1a, KEY_LEFT}, /* -> */ - - { 0x0a, KEY_CLEAR}, /* video default */ - { 0x0c, KEY_ZOOM}, /* hide pannel */ - { 0x47, KEY_SLEEP}, /* shutdown */ -}; -struct ir_scancode_table ir_codes_encore_enltv_fm53_table = { - .scan = ir_codes_encore_enltv_fm53, - .size = ARRAY_SIZE(ir_codes_encore_enltv_fm53), -}; -EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_fm53_table); - -/* Zogis Real Audio 220 - 32 keys IR */ -static struct ir_scancode ir_codes_real_audio_220_32_keys[] = { - { 0x1c, KEY_RADIO}, - { 0x12, KEY_POWER2}, - - { 0x01, KEY_1}, - { 0x02, KEY_2}, - { 0x03, KEY_3}, - { 0x04, KEY_4}, - { 0x05, KEY_5}, - { 0x06, KEY_6}, - { 0x07, KEY_7}, - { 0x08, KEY_8}, - { 0x09, KEY_9}, - { 0x00, KEY_0}, - - { 0x0c, KEY_VOLUMEUP}, - { 0x18, KEY_VOLUMEDOWN}, - { 0x0b, KEY_CHANNELUP}, - { 0x15, KEY_CHANNELDOWN}, - { 0x16, KEY_ENTER}, - - { 0x11, KEY_LIST}, /* Source */ - { 0x0d, KEY_AUDIO}, /* stereo */ - - { 0x0f, KEY_PREVIOUS}, /* Prev */ - { 0x1b, KEY_TIME}, /* Timeshift */ - { 0x1a, KEY_NEXT}, /* Next */ - - { 0x0e, KEY_STOP}, - { 0x1f, KEY_PLAY}, - { 0x1e, KEY_PLAYPAUSE}, /* Pause */ - - { 0x1d, KEY_RECORD}, - { 0x13, KEY_MUTE}, - { 0x19, KEY_CAMERA}, /* Snapshot */ - -}; -struct ir_scancode_table ir_codes_real_audio_220_32_keys_table = { - .scan = ir_codes_real_audio_220_32_keys, - .size = ARRAY_SIZE(ir_codes_real_audio_220_32_keys), -}; -EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys_table); - -/* ATI TV Wonder HD 600 USB - Devin Heitmueller <devin.heitmueller@gmail.com> - */ -static struct ir_scancode ir_codes_ati_tv_wonder_hd_600[] = { - { 0x00, KEY_RECORD}, /* Row 1 */ - { 0x01, KEY_PLAYPAUSE}, - { 0x02, KEY_STOP}, - { 0x03, KEY_POWER}, - { 0x04, KEY_PREVIOUS}, /* Row 2 */ - { 0x05, KEY_REWIND}, - { 0x06, KEY_FORWARD}, - { 0x07, KEY_NEXT}, - { 0x08, KEY_EPG}, /* Row 3 */ - { 0x09, KEY_HOME}, - { 0x0a, KEY_MENU}, - { 0x0b, KEY_CHANNELUP}, - { 0x0c, KEY_BACK}, /* Row 4 */ - { 0x0d, KEY_UP}, - { 0x0e, KEY_INFO}, - { 0x0f, KEY_CHANNELDOWN}, - { 0x10, KEY_LEFT}, /* Row 5 */ - { 0x11, KEY_SELECT}, - { 0x12, KEY_RIGHT}, - { 0x13, KEY_VOLUMEUP}, - { 0x14, KEY_LAST}, /* Row 6 */ - { 0x15, KEY_DOWN}, - { 0x16, KEY_MUTE}, - { 0x17, KEY_VOLUMEDOWN}, -}; -struct ir_scancode_table ir_codes_ati_tv_wonder_hd_600_table = { - .scan = ir_codes_ati_tv_wonder_hd_600, - .size = ARRAY_SIZE(ir_codes_ati_tv_wonder_hd_600), -}; -EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600_table); - -/* DVBWorld remotes - Igor M. Liplianin <liplianin@me.by> - */ -static struct ir_scancode ir_codes_dm1105_nec[] = { - { 0x0a, KEY_POWER2}, /* power */ - { 0x0c, KEY_MUTE}, /* mute */ - { 0x11, KEY_1}, - { 0x12, KEY_2}, - { 0x13, KEY_3}, - { 0x14, KEY_4}, - { 0x15, KEY_5}, - { 0x16, KEY_6}, - { 0x17, KEY_7}, - { 0x18, KEY_8}, - { 0x19, KEY_9}, - { 0x10, KEY_0}, - { 0x1c, KEY_CHANNELUP}, /* ch+ */ - { 0x0f, KEY_CHANNELDOWN}, /* ch- */ - { 0x1a, KEY_VOLUMEUP}, /* vol+ */ - { 0x0e, KEY_VOLUMEDOWN}, /* vol- */ - { 0x04, KEY_RECORD}, /* rec */ - { 0x09, KEY_CHANNEL}, /* fav */ - { 0x08, KEY_BACKSPACE}, /* rewind */ - { 0x07, KEY_FASTFORWARD}, /* fast */ - { 0x0b, KEY_PAUSE}, /* pause */ - { 0x02, KEY_ESC}, /* cancel */ - { 0x03, KEY_TAB}, /* tab */ - { 0x00, KEY_UP}, /* up */ - { 0x1f, KEY_ENTER}, /* ok */ - { 0x01, KEY_DOWN}, /* down */ - { 0x05, KEY_RECORD}, /* cap */ - { 0x06, KEY_STOP}, /* stop */ - { 0x40, KEY_ZOOM}, /* full */ - { 0x1e, KEY_TV}, /* tvmode */ - { 0x1b, KEY_B}, /* recall */ -}; -struct ir_scancode_table ir_codes_dm1105_nec_table = { - .scan = ir_codes_dm1105_nec, - .size = ARRAY_SIZE(ir_codes_dm1105_nec), -}; -EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table); - -static struct ir_scancode ir_codes_tevii_nec[] = { - { 0x0a, KEY_POWER2}, - { 0x0c, KEY_MUTE}, - { 0x11, KEY_1}, - { 0x12, KEY_2}, - { 0x13, KEY_3}, - { 0x14, KEY_4}, - { 0x15, KEY_5}, - { 0x16, KEY_6}, - { 0x17, KEY_7}, - { 0x18, KEY_8}, - { 0x19, KEY_9}, - { 0x10, KEY_0}, - { 0x1c, KEY_MENU}, - { 0x0f, KEY_VOLUMEDOWN}, - { 0x1a, KEY_LAST}, - { 0x0e, KEY_OPEN}, - { 0x04, KEY_RECORD}, - { 0x09, KEY_VOLUMEUP}, - { 0x08, KEY_CHANNELUP}, - { 0x07, KEY_PVR}, - { 0x0b, KEY_TIME}, - { 0x02, KEY_RIGHT}, - { 0x03, KEY_LEFT}, - { 0x00, KEY_UP}, - { 0x1f, KEY_OK}, - { 0x01, KEY_DOWN}, - { 0x05, KEY_TUNER}, - { 0x06, KEY_CHANNELDOWN}, - { 0x40, KEY_PLAYPAUSE}, - { 0x1e, KEY_REWIND}, - { 0x1b, KEY_FAVORITES}, - { 0x1d, KEY_BACK}, - { 0x4d, KEY_FASTFORWARD}, - { 0x44, KEY_EPG}, - { 0x4c, KEY_INFO}, - { 0x41, KEY_AB}, - { 0x43, KEY_AUDIO}, - { 0x45, KEY_SUBTITLE}, - { 0x4a, KEY_LIST}, - { 0x46, KEY_F1}, - { 0x47, KEY_F2}, - { 0x5e, KEY_F3}, - { 0x5c, KEY_F4}, - { 0x52, KEY_F5}, - { 0x5a, KEY_F6}, - { 0x56, KEY_MODE}, - { 0x58, KEY_SWITCHVIDEOMODE}, -}; -struct ir_scancode_table ir_codes_tevii_nec_table = { - .scan = ir_codes_tevii_nec, - .size = ARRAY_SIZE(ir_codes_tevii_nec), -}; -EXPORT_SYMBOL_GPL(ir_codes_tevii_nec_table); - -static struct ir_scancode ir_codes_tbs_nec[] = { - { 0x04, KEY_POWER2}, /*power*/ - { 0x14, KEY_MUTE}, /*mute*/ - { 0x07, KEY_1}, - { 0x06, KEY_2}, - { 0x05, KEY_3}, - { 0x0b, KEY_4}, - { 0x0a, KEY_5}, - { 0x09, KEY_6}, - { 0x0f, KEY_7}, - { 0x0e, KEY_8}, - { 0x0d, KEY_9}, - { 0x12, KEY_0}, - { 0x16, KEY_CHANNELUP}, /*ch+*/ - { 0x11, KEY_CHANNELDOWN},/*ch-*/ - { 0x13, KEY_VOLUMEUP}, /*vol+*/ - { 0x0c, KEY_VOLUMEDOWN},/*vol-*/ - { 0x03, KEY_RECORD}, /*rec*/ - { 0x18, KEY_PAUSE}, /*pause*/ - { 0x19, KEY_OK}, /*ok*/ - { 0x1a, KEY_CAMERA}, /* snapshot */ - { 0x01, KEY_UP}, - { 0x10, KEY_LEFT}, - { 0x02, KEY_RIGHT}, - { 0x08, KEY_DOWN}, - { 0x15, KEY_FAVORITES}, - { 0x17, KEY_SUBTITLE}, - { 0x1d, KEY_ZOOM}, - { 0x1f, KEY_EXIT}, - { 0x1e, KEY_MENU}, - { 0x1c, KEY_EPG}, - { 0x00, KEY_PREVIOUS}, - { 0x1b, KEY_MODE}, -}; -struct ir_scancode_table ir_codes_tbs_nec_table = { - .scan = ir_codes_tbs_nec, - .size = ARRAY_SIZE(ir_codes_tbs_nec), -}; -EXPORT_SYMBOL_GPL(ir_codes_tbs_nec_table); - -/* Terratec Cinergy Hybrid T USB XS - Devin Heitmueller <dheitmueller@linuxtv.org> - */ -static struct ir_scancode ir_codes_terratec_cinergy_xs[] = { - { 0x41, KEY_HOME}, - { 0x01, KEY_POWER}, - { 0x42, KEY_MENU}, - { 0x02, KEY_1}, - { 0x03, KEY_2}, - { 0x04, KEY_3}, - { 0x43, KEY_SUBTITLE}, - { 0x05, KEY_4}, - { 0x06, KEY_5}, - { 0x07, KEY_6}, - { 0x44, KEY_TEXT}, - { 0x08, KEY_7}, - { 0x09, KEY_8}, - { 0x0a, KEY_9}, - { 0x45, KEY_DELETE}, - { 0x0b, KEY_TUNER}, - { 0x0c, KEY_0}, - { 0x0d, KEY_MODE}, - { 0x46, KEY_TV}, - { 0x47, KEY_DVD}, - { 0x49, KEY_VIDEO}, - { 0x4b, KEY_AUX}, - { 0x10, KEY_UP}, - { 0x11, KEY_LEFT}, - { 0x12, KEY_OK}, - { 0x13, KEY_RIGHT}, - { 0x14, KEY_DOWN}, - { 0x0f, KEY_EPG}, - { 0x16, KEY_INFO}, - { 0x4d, KEY_BACKSPACE}, - { 0x1c, KEY_VOLUMEUP}, - { 0x4c, KEY_PLAY}, - { 0x1b, KEY_CHANNELUP}, - { 0x1e, KEY_VOLUMEDOWN}, - { 0x1d, KEY_MUTE}, - { 0x1f, KEY_CHANNELDOWN}, - { 0x17, KEY_RED}, - { 0x18, KEY_GREEN}, - { 0x19, KEY_YELLOW}, - { 0x1a, KEY_BLUE}, - { 0x58, KEY_RECORD}, - { 0x48, KEY_STOP}, - { 0x40, KEY_PAUSE}, - { 0x54, KEY_LAST}, - { 0x4e, KEY_REWIND}, - { 0x4f, KEY_FASTFORWARD}, - { 0x5c, KEY_NEXT}, -}; -struct ir_scancode_table ir_codes_terratec_cinergy_xs_table = { - .scan = ir_codes_terratec_cinergy_xs, - .size = ARRAY_SIZE(ir_codes_terratec_cinergy_xs), -}; -EXPORT_SYMBOL_GPL(ir_codes_terratec_cinergy_xs_table); - -/* EVGA inDtube - Devin Heitmueller <devin.heitmueller@gmail.com> - */ -static struct ir_scancode ir_codes_evga_indtube[] = { - { 0x12, KEY_POWER}, - { 0x02, KEY_MODE}, /* TV */ - { 0x14, KEY_MUTE}, - { 0x1a, KEY_CHANNELUP}, - { 0x16, KEY_TV2}, /* PIP */ - { 0x1d, KEY_VOLUMEUP}, - { 0x05, KEY_CHANNELDOWN}, - { 0x0f, KEY_PLAYPAUSE}, - { 0x19, KEY_VOLUMEDOWN}, - { 0x1c, KEY_REWIND}, - { 0x0d, KEY_RECORD}, - { 0x18, KEY_FORWARD}, - { 0x1e, KEY_PREVIOUS}, - { 0x1b, KEY_STOP}, - { 0x1f, KEY_NEXT}, - { 0x13, KEY_CAMERA}, -}; -struct ir_scancode_table ir_codes_evga_indtube_table = { - .scan = ir_codes_evga_indtube, - .size = ARRAY_SIZE(ir_codes_evga_indtube), -}; -EXPORT_SYMBOL_GPL(ir_codes_evga_indtube_table); - -static struct ir_scancode ir_codes_videomate_s350[] = { - { 0x00, KEY_TV}, - { 0x01, KEY_DVD}, - { 0x04, KEY_RECORD}, - { 0x05, KEY_VIDEO}, /* TV/Video */ - { 0x07, KEY_STOP}, - { 0x08, KEY_PLAYPAUSE}, - { 0x0a, KEY_REWIND}, - { 0x0f, KEY_FASTFORWARD}, - { 0x10, KEY_CHANNELUP}, - { 0x12, KEY_VOLUMEUP}, - { 0x13, KEY_CHANNELDOWN}, - { 0x14, KEY_MUTE}, - { 0x15, KEY_VOLUMEDOWN}, - { 0x16, KEY_1}, - { 0x17, KEY_2}, - { 0x18, KEY_3}, - { 0x19, KEY_4}, - { 0x1a, KEY_5}, - { 0x1b, KEY_6}, - { 0x1c, KEY_7}, - { 0x1d, KEY_8}, - { 0x1e, KEY_9}, - { 0x1f, KEY_0}, - { 0x21, KEY_SLEEP}, - { 0x24, KEY_ZOOM}, - { 0x25, KEY_LAST}, /* Recall */ - { 0x26, KEY_SUBTITLE}, /* CC */ - { 0x27, KEY_LANGUAGE}, /* MTS */ - { 0x29, KEY_CHANNEL}, /* SURF */ - { 0x2b, KEY_A}, - { 0x2c, KEY_B}, - { 0x2f, KEY_CAMERA}, /* Snapshot */ - { 0x23, KEY_RADIO}, - { 0x02, KEY_PREVIOUSSONG}, - { 0x06, KEY_NEXTSONG}, - { 0x03, KEY_EPG}, - { 0x09, KEY_SETUP}, - { 0x22, KEY_BACKSPACE}, - { 0x0c, KEY_UP}, - { 0x0e, KEY_DOWN}, - { 0x0b, KEY_LEFT}, - { 0x0d, KEY_RIGHT}, - { 0x11, KEY_ENTER}, - { 0x20, KEY_TEXT}, -}; -struct ir_scancode_table ir_codes_videomate_s350_table = { - .scan = ir_codes_videomate_s350, - .size = ARRAY_SIZE(ir_codes_videomate_s350), -}; -EXPORT_SYMBOL_GPL(ir_codes_videomate_s350_table); - -/* GADMEI UTV330+ RM008Z remote - Shine Liu <shinel@foxmail.com> - */ -static struct ir_scancode ir_codes_gadmei_rm008z[] = { - { 0x14, KEY_POWER2}, /* POWER OFF */ - { 0x0c, KEY_MUTE}, /* MUTE */ - - { 0x18, KEY_TV}, /* TV */ - { 0x0e, KEY_VIDEO}, /* AV */ - { 0x0b, KEY_AUDIO}, /* SV */ - { 0x0f, KEY_RADIO}, /* FM */ - - { 0x00, KEY_1}, - { 0x01, KEY_2}, - { 0x02, KEY_3}, - { 0x03, KEY_4}, - { 0x04, KEY_5}, - { 0x05, KEY_6}, - { 0x06, KEY_7}, - { 0x07, KEY_8}, - { 0x08, KEY_9}, - { 0x09, KEY_0}, - { 0x0a, KEY_INFO}, /* OSD */ - { 0x1c, KEY_BACKSPACE}, /* LAST */ - - { 0x0d, KEY_PLAY}, /* PLAY */ - { 0x1e, KEY_CAMERA}, /* SNAPSHOT */ - { 0x1a, KEY_RECORD}, /* RECORD */ - { 0x17, KEY_STOP}, /* STOP */ - - { 0x1f, KEY_UP}, /* UP */ - { 0x44, KEY_DOWN}, /* DOWN */ - { 0x46, KEY_TAB}, /* BACK */ - { 0x4a, KEY_ZOOM}, /* FULLSECREEN */ - - { 0x10, KEY_VOLUMEUP}, /* VOLUMEUP */ - { 0x11, KEY_VOLUMEDOWN}, /* VOLUMEDOWN */ - { 0x12, KEY_CHANNELUP}, /* CHANNELUP */ - { 0x13, KEY_CHANNELDOWN}, /* CHANNELDOWN */ - { 0x15, KEY_ENTER}, /* OK */ -}; -struct ir_scancode_table ir_codes_gadmei_rm008z_table = { - .scan = ir_codes_gadmei_rm008z, - .size = ARRAY_SIZE(ir_codes_gadmei_rm008z), -}; -EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table); - -/************************************************************* - * COMPLETE SCANCODE TABLES - * Instead of just a partial scancode, the tables bellow - * contains the complete scancode and the receiver protocol - *************************************************************/ - -/* - * Hauppauge:the newer, gray remotes (seems there are multiple - * slightly different versions), shipped with cx88+ivtv cards. - * - * This table contains the complete RC5 code, instead of just the data part - */ -static struct ir_scancode ir_codes_rc5_hauppauge_new[] = { - /* Keys 0 to 9 */ - { 0x1e00, KEY_0 }, - { 0x1e01, KEY_1 }, - { 0x1e02, KEY_2 }, - { 0x1e03, KEY_3 }, - { 0x1e04, KEY_4 }, - { 0x1e05, KEY_5 }, - { 0x1e06, KEY_6 }, - { 0x1e07, KEY_7 }, - { 0x1e08, KEY_8 }, - { 0x1e09, KEY_9 }, - - { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */ - { 0x1e0b, KEY_RED }, /* red button */ - { 0x1e0c, KEY_RADIO }, - { 0x1e0d, KEY_MENU }, - { 0x1e0e, KEY_SUBTITLE }, /* also the # key */ - { 0x1e0f, KEY_MUTE }, - { 0x1e10, KEY_VOLUMEUP }, - { 0x1e11, KEY_VOLUMEDOWN }, - { 0x1e12, KEY_PREVIOUS }, /* previous channel */ - { 0x1e14, KEY_UP }, - { 0x1e15, KEY_DOWN }, - { 0x1e16, KEY_LEFT }, - { 0x1e17, KEY_RIGHT }, - { 0x1e18, KEY_VIDEO }, /* Videos */ - { 0x1e19, KEY_AUDIO }, /* Music */ - /* 0x1e1a: Pictures - presume this means - "Multimedia Home Platform" - - no "PICTURES" key in input.h - */ - { 0x1e1a, KEY_MHP }, - - { 0x1e1b, KEY_EPG }, /* Guide */ - { 0x1e1c, KEY_TV }, - { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ - { 0x1e1f, KEY_EXIT }, /* back/exit */ - { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ - { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ - { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */ - { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ - { 0x1e25, KEY_ENTER }, /* OK */ - { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */ - { 0x1e29, KEY_BLUE }, /* blue key */ - { 0x1e2e, KEY_GREEN }, /* green button */ - { 0x1e30, KEY_PAUSE }, /* pause */ - { 0x1e32, KEY_REWIND }, /* backward << */ - { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ - { 0x1e35, KEY_PLAY }, - { 0x1e36, KEY_STOP }, - { 0x1e37, KEY_RECORD }, /* recording */ - { 0x1e38, KEY_YELLOW }, /* yellow key */ - { 0x1e3b, KEY_SELECT }, /* top right button */ - { 0x1e3c, KEY_ZOOM }, /* full */ - { 0x1e3d, KEY_POWER }, /* system power (green button) */ -}; - -struct ir_scancode_table ir_codes_rc5_hauppauge_new_table = { - .scan = ir_codes_rc5_hauppauge_new, - .size = ARRAY_SIZE(ir_codes_rc5_hauppauge_new), - .ir_type = IR_TYPE_RC5, -}; -EXPORT_SYMBOL_GPL(ir_codes_rc5_hauppauge_new_table); - -/* Terratec Cinergy Hybrid T USB XS FM - Mauro Carvalho Chehab <mchehab@redhat.com> - */ -static struct ir_scancode ir_codes_nec_terratec_cinergy_xs[] = { - { 0x1441, KEY_HOME}, - { 0x1401, KEY_POWER2}, - - { 0x1442, KEY_MENU}, /* DVD menu */ - { 0x1443, KEY_SUBTITLE}, - { 0x1444, KEY_TEXT}, /* Teletext */ - { 0x1445, KEY_DELETE}, - - { 0x1402, KEY_1}, - { 0x1403, KEY_2}, - { 0x1404, KEY_3}, - { 0x1405, KEY_4}, - { 0x1406, KEY_5}, - { 0x1407, KEY_6}, - { 0x1408, KEY_7}, - { 0x1409, KEY_8}, - { 0x140a, KEY_9}, - { 0x140c, KEY_0}, - - { 0x140b, KEY_TUNER}, /* AV */ - { 0x140d, KEY_MODE}, /* A.B */ - - { 0x1446, KEY_TV}, - { 0x1447, KEY_DVD}, - { 0x1449, KEY_VIDEO}, - { 0x144a, KEY_RADIO}, /* Music */ - { 0x144b, KEY_CAMERA}, /* PIC */ - - { 0x1410, KEY_UP}, - { 0x1411, KEY_LEFT}, - { 0x1412, KEY_OK}, - { 0x1413, KEY_RIGHT}, - { 0x1414, KEY_DOWN}, - - { 0x140f, KEY_EPG}, - { 0x1416, KEY_INFO}, - { 0x144d, KEY_BACKSPACE}, - - { 0x141c, KEY_VOLUMEUP}, - { 0x141e, KEY_VOLUMEDOWN}, - - { 0x144c, KEY_PLAY}, - { 0x141d, KEY_MUTE}, - - { 0x141b, KEY_CHANNELUP}, - { 0x141f, KEY_CHANNELDOWN}, - - { 0x1417, KEY_RED}, - { 0x1418, KEY_GREEN}, - { 0x1419, KEY_YELLOW}, - { 0x141a, KEY_BLUE}, - - { 0x1458, KEY_RECORD}, - { 0x1448, KEY_STOP}, - { 0x1440, KEY_PAUSE}, - - { 0x1454, KEY_LAST}, - { 0x144e, KEY_REWIND}, - { 0x144f, KEY_FASTFORWARD}, - { 0x145c, KEY_NEXT}, -}; -struct ir_scancode_table ir_codes_nec_terratec_cinergy_xs_table = { - .scan = ir_codes_nec_terratec_cinergy_xs, - .size = ARRAY_SIZE(ir_codes_nec_terratec_cinergy_xs), - .ir_type = IR_TYPE_NEC, -}; -EXPORT_SYMBOL_GPL(ir_codes_nec_terratec_cinergy_xs_table); - - -/* Leadtek Winfast TV USB II Deluxe remote - Magnus Alm <magnus.alm@gmail.com> - */ -static struct ir_scancode ir_codes_winfast_usbii_deluxe[] = { - { 0x62, KEY_0}, - { 0x75, KEY_1}, - { 0x76, KEY_2}, - { 0x77, KEY_3}, - { 0x79, KEY_4}, - { 0x7a, KEY_5}, - { 0x7b, KEY_6}, - { 0x7d, KEY_7}, - { 0x7e, KEY_8}, - { 0x7f, KEY_9}, - - { 0x38, KEY_CAMERA}, /* SNAPSHOT */ - { 0x37, KEY_RECORD}, /* RECORD */ - { 0x35, KEY_TIME}, /* TIMESHIFT */ - - { 0x74, KEY_VOLUMEUP}, /* VOLUMEUP */ - { 0x78, KEY_VOLUMEDOWN}, /* VOLUMEDOWN */ - { 0x64, KEY_MUTE}, /* MUTE */ - - { 0x21, KEY_CHANNEL}, /* SURF */ - { 0x7c, KEY_CHANNELUP}, /* CHANNELUP */ - { 0x60, KEY_CHANNELDOWN}, /* CHANNELDOWN */ - { 0x61, KEY_LAST}, /* LAST CHANNEL (RECALL) */ - - { 0x72, KEY_VIDEO}, /* INPUT MODES (TV/FM) */ - - { 0x70, KEY_POWER2}, /* TV ON/OFF */ - - { 0x39, KEY_CYCLEWINDOWS}, /* MINIMIZE (BOSS) */ - { 0x3a, KEY_NEW}, /* PIP */ - { 0x73, KEY_ZOOM}, /* FULLSECREEN */ - - { 0x66, KEY_INFO}, /* OSD (DISPLAY) */ - - { 0x31, KEY_DOT}, /* '.' */ - { 0x63, KEY_ENTER}, /* ENTER */ - -}; -struct ir_scancode_table ir_codes_winfast_usbii_deluxe_table = { - .scan = ir_codes_winfast_usbii_deluxe, - .size = ARRAY_SIZE(ir_codes_winfast_usbii_deluxe), -}; -EXPORT_SYMBOL_GPL(ir_codes_winfast_usbii_deluxe_table); - -/* Kworld 315U - */ -static struct ir_scancode ir_codes_kworld_315u[] = { - { 0x6143, KEY_POWER }, - { 0x6101, KEY_TUNER }, /* source */ - { 0x610b, KEY_ZOOM }, - { 0x6103, KEY_POWER2 }, /* shutdown */ - - { 0x6104, KEY_1 }, - { 0x6108, KEY_2 }, - { 0x6102, KEY_3 }, - { 0x6109, KEY_CHANNELUP }, - - { 0x610f, KEY_4 }, - { 0x6105, KEY_5 }, - { 0x6106, KEY_6 }, - { 0x6107, KEY_CHANNELDOWN }, - - { 0x610c, KEY_7 }, - { 0x610d, KEY_8 }, - { 0x610a, KEY_9 }, - { 0x610e, KEY_VOLUMEUP }, - - { 0x6110, KEY_LAST }, - { 0x6111, KEY_0 }, - { 0x6112, KEY_ENTER }, - { 0x6113, KEY_VOLUMEDOWN }, - - { 0x6114, KEY_RECORD }, - { 0x6115, KEY_STOP }, - { 0x6116, KEY_PLAY }, - { 0x6117, KEY_MUTE }, - - { 0x6118, KEY_UP }, - { 0x6119, KEY_DOWN }, - { 0x611a, KEY_LEFT }, - { 0x611b, KEY_RIGHT }, - - { 0x611c, KEY_RED }, - { 0x611d, KEY_GREEN }, - { 0x611e, KEY_YELLOW }, - { 0x611f, KEY_BLUE }, -}; - -struct ir_scancode_table ir_codes_kworld_315u_table = { - .scan = ir_codes_kworld_315u, - .size = ARRAY_SIZE(ir_codes_kworld_315u), - .ir_type = IR_TYPE_NEC, -}; -EXPORT_SYMBOL_GPL(ir_codes_kworld_315u_table); diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index bfca26d51827..9374a006f43d 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -1,4 +1,4 @@ -/* ir-register.c - handle IR scancode->keycode tables +/* ir-keytable.c - handle IR scancode->keycode tables * * Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com> * @@ -15,384 +15,408 @@ #include <linux/input.h> #include <linux/slab.h> -#include <media/ir-common.h> +#include "ir-core-priv.h" -#define IR_TAB_MIN_SIZE 32 -#define IR_TAB_MAX_SIZE 1024 +/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */ +#define IR_TAB_MIN_SIZE 256 +#define IR_TAB_MAX_SIZE 8192 + +/* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */ +#define IR_KEYPRESS_TIMEOUT 250 /** - * ir_seek_table() - returns the element order on the table - * @rc_tab: the ir_scancode_table with the keymap to be used - * @scancode: the scancode that we're seeking + * ir_resize_table() - resizes a scancode table if necessary + * @rc_tab: the ir_scancode_table to resize + * @return: zero on success or a negative error code * - * This routine is used by the input routines when a key is pressed at the - * IR. The scancode is received and needs to be converted into a keycode. - * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the - * corresponding keycode from the table. + * This routine will shrink the ir_scancode_table if it has lots of + * unused entries and grow it if it is full. */ -static int ir_seek_table(struct ir_scancode_table *rc_tab, u32 scancode) +static int ir_resize_table(struct ir_scancode_table *rc_tab) { - int rc; - unsigned long flags; - struct ir_scancode *keymap = rc_tab->scan; + unsigned int oldalloc = rc_tab->alloc; + unsigned int newalloc = oldalloc; + struct ir_scancode *oldscan = rc_tab->scan; + struct ir_scancode *newscan; + + if (rc_tab->size == rc_tab->len) { + /* All entries in use -> grow keytable */ + if (rc_tab->alloc >= IR_TAB_MAX_SIZE) + return -ENOMEM; - spin_lock_irqsave(&rc_tab->lock, flags); + newalloc *= 2; + IR_dprintk(1, "Growing table to %u bytes\n", newalloc); + } - /* FIXME: replace it by a binary search */ + if ((rc_tab->len * 3 < rc_tab->size) && (oldalloc > IR_TAB_MIN_SIZE)) { + /* Less than 1/3 of entries in use -> shrink keytable */ + newalloc /= 2; + IR_dprintk(1, "Shrinking table to %u bytes\n", newalloc); + } - for (rc = 0; rc < rc_tab->size; rc++) - if (keymap[rc].scancode == scancode) - goto exit; + if (newalloc == oldalloc) + return 0; - /* Not found */ - rc = -EINVAL; + newscan = kmalloc(newalloc, GFP_ATOMIC); + if (!newscan) { + IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc); + return -ENOMEM; + } -exit: - spin_unlock_irqrestore(&rc_tab->lock, flags); - return rc; + memcpy(newscan, rc_tab->scan, rc_tab->len * sizeof(struct ir_scancode)); + rc_tab->scan = newscan; + rc_tab->alloc = newalloc; + rc_tab->size = rc_tab->alloc / sizeof(struct ir_scancode); + kfree(oldscan); + return 0; } /** - * ir_roundup_tablesize() - gets an optimum value for the table size - * @n_elems: minimum number of entries to store keycodes - * - * This routine is used to choose the keycode table size. + * ir_do_setkeycode() - internal function to set a keycode in the + * scancode->keycode table + * @dev: the struct input_dev device descriptor + * @rc_tab: the struct ir_scancode_table to set the keycode in + * @scancode: the scancode for the ir command + * @keycode: the keycode for the ir command + * @resize: whether the keytable may be shrunk + * @return: -EINVAL if the keycode could not be inserted, otherwise zero. * - * In order to have some empty space for new keycodes, - * and knowing in advance that kmalloc allocates only power of two - * segments, it optimizes the allocated space to have some spare space - * for those new keycodes by using the maximum number of entries that - * will be effectively be allocated by kmalloc. - * In order to reduce the quantity of table resizes, it has a minimum - * table size of IR_TAB_MIN_SIZE. + * This routine is used internally to manipulate the scancode->keycode table. + * The caller has to hold @rc_tab->lock. */ -static int ir_roundup_tablesize(int n_elems) +static int ir_do_setkeycode(struct input_dev *dev, + struct ir_scancode_table *rc_tab, + unsigned scancode, unsigned keycode, + bool resize) { - size_t size; - - if (n_elems < IR_TAB_MIN_SIZE) - n_elems = IR_TAB_MIN_SIZE; + unsigned int i; + int old_keycode = KEY_RESERVED; + struct ir_input_dev *ir_dev = input_get_drvdata(dev); /* - * As kmalloc only allocates sizes of power of two, get as - * much entries as possible for the allocated memory segment + * Unfortunately, some hardware-based IR decoders don't provide + * all bits for the complete IR code. In general, they provide only + * the command part of the IR code. Yet, as it is possible to replace + * the provided IR with another one, it is needed to allow loading + * IR tables from other remotes. So, */ - size = roundup_pow_of_two(n_elems * sizeof(struct ir_scancode)); - n_elems = size / sizeof(struct ir_scancode); + if (ir_dev->props && ir_dev->props->scanmask) { + scancode &= ir_dev->props->scanmask; + } - return n_elems; + /* First check if we already have a mapping for this ir command */ + for (i = 0; i < rc_tab->len; i++) { + /* Keytable is sorted from lowest to highest scancode */ + if (rc_tab->scan[i].scancode > scancode) + break; + else if (rc_tab->scan[i].scancode < scancode) + continue; + + old_keycode = rc_tab->scan[i].keycode; + rc_tab->scan[i].keycode = keycode; + + /* Did the user wish to remove the mapping? */ + if (keycode == KEY_RESERVED || keycode == KEY_UNKNOWN) { + IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", + i, scancode); + rc_tab->len--; + memmove(&rc_tab->scan[i], &rc_tab->scan[i + 1], + (rc_tab->len - i) * sizeof(struct ir_scancode)); + } + + /* Possibly shrink the keytable, failure is not a problem */ + ir_resize_table(rc_tab); + break; + } + + if (old_keycode == KEY_RESERVED && keycode != KEY_RESERVED) { + /* No previous mapping found, we might need to grow the table */ + if (resize && ir_resize_table(rc_tab)) + return -ENOMEM; + + IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n", + i, scancode, keycode); + + /* i is the proper index to insert our new keycode */ + memmove(&rc_tab->scan[i + 1], &rc_tab->scan[i], + (rc_tab->len - i) * sizeof(struct ir_scancode)); + rc_tab->scan[i].scancode = scancode; + rc_tab->scan[i].keycode = keycode; + rc_tab->len++; + set_bit(keycode, dev->keybit); + } else { + IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", + i, scancode, keycode); + /* A previous mapping was updated... */ + clear_bit(old_keycode, dev->keybit); + /* ...but another scancode might use the same keycode */ + for (i = 0; i < rc_tab->len; i++) { + if (rc_tab->scan[i].keycode == old_keycode) { + set_bit(old_keycode, dev->keybit); + break; + } + } + } + + return 0; } /** - * ir_copy_table() - copies a keytable, discarding the unused entries - * @destin: destin table - * @origin: origin table + * ir_setkeycode() - set a keycode in the scancode->keycode table + * @dev: the struct input_dev device descriptor + * @scancode: the desired scancode + * @keycode: result + * @return: -EINVAL if the keycode could not be inserted, otherwise zero. * - * Copies all entries where the keycode is not KEY_UNKNOWN/KEY_RESERVED - * Also copies table size and table protocol. - * NOTE: It shouldn't copy the lock field + * This routine is used to handle evdev EVIOCSKEY ioctl. */ - -static int ir_copy_table(struct ir_scancode_table *destin, - const struct ir_scancode_table *origin) +static int ir_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) { - int i, j = 0; - - for (i = 0; i < origin->size; i++) { - if (origin->scan[i].keycode == KEY_UNKNOWN || - origin->scan[i].keycode == KEY_RESERVED) - continue; + int rc; + unsigned long flags; + struct ir_input_dev *ir_dev = input_get_drvdata(dev); + struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - memcpy(&destin->scan[j], &origin->scan[i], sizeof(struct ir_scancode)); - j++; - } - destin->size = j; - destin->ir_type = origin->ir_type; + spin_lock_irqsave(&rc_tab->lock, flags); + rc = ir_do_setkeycode(dev, rc_tab, scancode, keycode, true); + spin_unlock_irqrestore(&rc_tab->lock, flags); + return rc; +} - IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", destin->size); +/** + * ir_setkeytable() - sets several entries in the scancode->keycode table + * @dev: the struct input_dev device descriptor + * @to: the struct ir_scancode_table to copy entries to + * @from: the struct ir_scancode_table to copy entries from + * @return: -EINVAL if all keycodes could not be inserted, otherwise zero. + * + * This routine is used to handle table initialization. + */ +static int ir_setkeytable(struct input_dev *dev, + struct ir_scancode_table *to, + const struct ir_scancode_table *from) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(dev); + struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; + unsigned long flags; + unsigned int i; + int rc = 0; - return 0; + spin_lock_irqsave(&rc_tab->lock, flags); + for (i = 0; i < from->size; i++) { + rc = ir_do_setkeycode(dev, to, from->scan[i].scancode, + from->scan[i].keycode, false); + if (rc) + break; + } + spin_unlock_irqrestore(&rc_tab->lock, flags); + return rc; } /** - * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table + * ir_getkeycode() - get a keycode from the scancode->keycode table * @dev: the struct input_dev device descriptor * @scancode: the desired scancode - * @keycode: the keycode to be retorned. + * @keycode: used to return the keycode, if found, or KEY_RESERVED + * @return: always returns zero. * * This routine is used to handle evdev EVIOCGKEY ioctl. - * If the key is not found, returns -EINVAL, otherwise, returns 0. */ static int ir_getkeycode(struct input_dev *dev, unsigned int scancode, unsigned int *keycode) { - int elem; + int start, end, mid; + unsigned long flags; + int key = KEY_RESERVED; struct ir_input_dev *ir_dev = input_get_drvdata(dev); struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - elem = ir_seek_table(rc_tab, scancode); - if (elem >= 0) { - *keycode = rc_tab->scan[elem].keycode; - return 0; + spin_lock_irqsave(&rc_tab->lock, flags); + start = 0; + end = rc_tab->len - 1; + while (start <= end) { + mid = (start + end) / 2; + if (rc_tab->scan[mid].scancode < scancode) + start = mid + 1; + else if (rc_tab->scan[mid].scancode > scancode) + end = mid - 1; + else { + key = rc_tab->scan[mid].keycode; + break; + } } + spin_unlock_irqrestore(&rc_tab->lock, flags); - /* - * Scancode not found and table can't be expanded - */ - if (elem < 0 && rc_tab->size == IR_TAB_MAX_SIZE) - return -EINVAL; + if (key == KEY_RESERVED) + IR_dprintk(1, "unknown key for scancode 0x%04x\n", + scancode); - /* - * If is there extra space, returns KEY_RESERVED, - * otherwise, input core won't let ir_setkeycode to work - */ - *keycode = KEY_RESERVED; + *keycode = key; return 0; } /** - * ir_is_resize_needed() - Check if the table needs rezise - * @table: keycode table that may need to resize - * @n_elems: minimum number of entries to store keycodes - * - * Considering that kmalloc uses power of two storage areas, this - * routine detects if the real alloced size will change. If not, it - * just returns without doing nothing. Otherwise, it will extend or - * reduce the table size to meet the new needs. + * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode + * @input_dev: the struct input_dev descriptor of the device + * @scancode: the scancode that we're seeking * - * It returns 0 if no resize is needed, 1 otherwise. + * This routine is used by the input routines when a key is pressed at the + * IR. The scancode is received and needs to be converted into a keycode. + * If the key is not found, it returns KEY_RESERVED. Otherwise, returns the + * corresponding keycode from the table. */ -static int ir_is_resize_needed(struct ir_scancode_table *table, int n_elems) +u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) { - int cur_size = ir_roundup_tablesize(table->size); - int new_size = ir_roundup_tablesize(n_elems); - - if (cur_size == new_size) - return 0; + int keycode; - /* Resize is needed */ - return 1; + ir_getkeycode(dev, scancode, &keycode); + if (keycode != KEY_RESERVED) + IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", + dev->name, scancode, keycode); + return keycode; } +EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); /** - * ir_delete_key() - remove a keycode from the table - * @rc_tab: keycode table - * @elem: element to be removed + * ir_keyup() - generates input event to cleanup a key press + * @ir: the struct ir_input_dev descriptor of the device * + * This routine is used to signal that a key has been released on the + * remote control. It reports a keyup input event via input_report_key(). */ -static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem) +static void ir_keyup(struct ir_input_dev *ir) { - unsigned long flags = 0; - int newsize = rc_tab->size - 1; - int resize = ir_is_resize_needed(rc_tab, newsize); - struct ir_scancode *oldkeymap = rc_tab->scan; - struct ir_scancode *newkeymap = NULL; - - if (resize) - newkeymap = kzalloc(ir_roundup_tablesize(newsize) * - sizeof(*newkeymap), GFP_ATOMIC); - - /* There's no memory for resize. Keep the old table */ - if (!resize || !newkeymap) { - newkeymap = oldkeymap; - - /* We'll modify the live table. Lock it */ - spin_lock_irqsave(&rc_tab->lock, flags); - } + if (!ir->keypressed) + return; - /* - * Copy the elements before the one that will be deleted - * if (!resize), both oldkeymap and newkeymap points - * to the same place, so, there's no need to copy - */ - if (resize && elem > 0) - memcpy(newkeymap, oldkeymap, - elem * sizeof(*newkeymap)); + IR_dprintk(1, "keyup key 0x%04x\n", ir->last_keycode); + input_report_key(ir->input_dev, ir->last_keycode, 0); + input_sync(ir->input_dev); + ir->keypressed = false; +} + +/** + * ir_timer_keyup() - generates a keyup event after a timeout + * @cookie: a pointer to struct ir_input_dev passed to setup_timer() + * + * This routine will generate a keyup event some time after a keydown event + * is generated when no further activity has been detected. + */ +static void ir_timer_keyup(unsigned long cookie) +{ + struct ir_input_dev *ir = (struct ir_input_dev *)cookie; + unsigned long flags; /* - * Copy the other elements overwriting the element to be removed - * This operation applies to both resize and non-resize case + * ir->keyup_jiffies is used to prevent a race condition if a + * hardware interrupt occurs at this point and the keyup timer + * event is moved further into the future as a result. + * + * The timer will then be reactivated and this function called + * again in the future. We need to exit gracefully in that case + * to allow the input subsystem to do its auto-repeat magic or + * a keyup event might follow immediately after the keydown. */ - if (elem < newsize) - memcpy(&newkeymap[elem], &oldkeymap[elem + 1], - (newsize - elem) * sizeof(*newkeymap)); - - if (resize) { - /* - * As the copy happened to a temporary table, only here - * it needs to lock while replacing the table pointers - * to use the new table - */ - spin_lock_irqsave(&rc_tab->lock, flags); - rc_tab->size = newsize; - rc_tab->scan = newkeymap; - spin_unlock_irqrestore(&rc_tab->lock, flags); - - /* Frees the old keytable */ - kfree(oldkeymap); - } else { - rc_tab->size = newsize; - spin_unlock_irqrestore(&rc_tab->lock, flags); - } + spin_lock_irqsave(&ir->keylock, flags); + if (time_is_after_eq_jiffies(ir->keyup_jiffies)) + ir_keyup(ir); + spin_unlock_irqrestore(&ir->keylock, flags); } /** - * ir_insert_key() - insert a keycode at the table - * @rc_tab: keycode table - * @scancode: the desired scancode - * @keycode: the keycode to be retorned. + * ir_repeat() - notifies the IR core that a key is still pressed + * @dev: the struct input_dev descriptor of the device * + * This routine is used by IR decoders when a repeat message which does + * not include the necessary bits to reproduce the scancode has been + * received. */ -static int ir_insert_key(struct ir_scancode_table *rc_tab, - int scancode, int keycode) +void ir_repeat(struct input_dev *dev) { unsigned long flags; - int elem = rc_tab->size; - int newsize = rc_tab->size + 1; - int resize = ir_is_resize_needed(rc_tab, newsize); - struct ir_scancode *oldkeymap = rc_tab->scan; - struct ir_scancode *newkeymap; - - if (resize) { - newkeymap = kzalloc(ir_roundup_tablesize(newsize) * - sizeof(*newkeymap), GFP_ATOMIC); - if (!newkeymap) - return -ENOMEM; + struct ir_input_dev *ir = input_get_drvdata(dev); - memcpy(newkeymap, oldkeymap, - rc_tab->size * sizeof(*newkeymap)); - } else - newkeymap = oldkeymap; + spin_lock_irqsave(&ir->keylock, flags); - /* Stores the new code at the table */ - IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n", - rc_tab->size, scancode, keycode); + if (!ir->keypressed) + goto out; - spin_lock_irqsave(&rc_tab->lock, flags); - rc_tab->size = newsize; - if (resize) { - rc_tab->scan = newkeymap; - kfree(oldkeymap); - } - newkeymap[elem].scancode = scancode; - newkeymap[elem].keycode = keycode; - spin_unlock_irqrestore(&rc_tab->lock, flags); + ir->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); + mod_timer(&ir->timer_keyup, ir->keyup_jiffies); - return 0; +out: + spin_unlock_irqrestore(&ir->keylock, flags); } +EXPORT_SYMBOL_GPL(ir_repeat); /** - * ir_setkeycode() - set a keycode at the evdev scancode ->keycode table - * @dev: the struct input_dev device descriptor - * @scancode: the desired scancode - * @keycode: the keycode to be retorned. + * ir_keydown() - generates input event for a key press + * @dev: the struct input_dev descriptor of the device + * @scancode: the scancode that we're seeking + * @toggle: the toggle value (protocol dependent, if the protocol doesn't + * support toggle values, this should be set to zero) * - * This routine is used to handle evdev EVIOCSKEY ioctl. - * There's one caveat here: how can we increase the size of the table? - * If the key is not found, returns -EINVAL, otherwise, returns 0. + * This routine is used by the input routines when a key is pressed at the + * IR. It gets the keycode for a scancode and reports an input event via + * input_report_key(). */ -static int ir_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) +void ir_keydown(struct input_dev *dev, int scancode, u8 toggle) { - int rc = 0; - struct ir_input_dev *ir_dev = input_get_drvdata(dev); - struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - struct ir_scancode *keymap = rc_tab->scan; unsigned long flags; + struct ir_input_dev *ir = input_get_drvdata(dev); - /* - * Handle keycode table deletions - * - * If userspace is adding a KEY_UNKNOWN or KEY_RESERVED, - * deal as a trial to remove an existing scancode attribution - * if table become too big, reduce it to save space - */ - if (keycode == KEY_UNKNOWN || keycode == KEY_RESERVED) { - rc = ir_seek_table(rc_tab, scancode); - if (rc < 0) - return 0; - - IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", rc, scancode); - clear_bit(keymap[rc].keycode, dev->keybit); - ir_delete_key(rc_tab, rc); - - return 0; - } + u32 keycode = ir_g_keycode_from_table(dev, scancode); - /* - * Handle keycode replacements - * - * If the scancode exists, just replace by the new value - */ - rc = ir_seek_table(rc_tab, scancode); - if (rc >= 0) { - IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", - rc, scancode, keycode); + spin_lock_irqsave(&ir->keylock, flags); - clear_bit(keymap[rc].keycode, dev->keybit); + /* Repeat event? */ + if (ir->keypressed && + ir->last_scancode == scancode && + ir->last_toggle == toggle) + goto set_timer; - spin_lock_irqsave(&rc_tab->lock, flags); - keymap[rc].keycode = keycode; - spin_unlock_irqrestore(&rc_tab->lock, flags); + /* Release old keypress */ + ir_keyup(ir); - set_bit(keycode, dev->keybit); + ir->last_scancode = scancode; + ir->last_toggle = toggle; + ir->last_keycode = keycode; - return 0; - } + if (keycode == KEY_RESERVED) + goto out; - /* - * Handle new scancode inserts - * - * reallocate table if needed and insert a new keycode - */ + /* Register a keypress */ + ir->keypressed = true; + IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n", + dev->name, keycode, scancode); + input_report_key(dev, ir->last_keycode, 1); + input_sync(dev); - /* Avoid growing the table indefinitely */ - if (rc_tab->size + 1 > IR_TAB_MAX_SIZE) - return -EINVAL; - - rc = ir_insert_key(rc_tab, scancode, keycode); - if (rc < 0) - return rc; - set_bit(keycode, dev->keybit); - - return 0; +set_timer: + ir->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); + mod_timer(&ir->timer_keyup, ir->keyup_jiffies); +out: + spin_unlock_irqrestore(&ir->keylock, flags); } +EXPORT_SYMBOL_GPL(ir_keydown); -/** - * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode - * @input_dev: the struct input_dev descriptor of the device - * @scancode: the scancode that we're seeking - * - * This routine is used by the input routines when a key is pressed at the - * IR. The scancode is received and needs to be converted into a keycode. - * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the - * corresponding keycode from the table. - */ -u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) +static int ir_open(struct input_dev *input_dev) { - struct ir_input_dev *ir_dev = input_get_drvdata(dev); - struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - struct ir_scancode *keymap = rc_tab->scan; - int elem; + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - elem = ir_seek_table(rc_tab, scancode); - if (elem >= 0) { - IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", - dev->name, scancode, keymap[elem].keycode); - - return rc_tab->scan[elem].keycode; - } + return ir_dev->props->open(ir_dev->props->priv); +} - printk(KERN_INFO "%s: unknown key for scancode 0x%04x\n", - dev->name, scancode); +static void ir_close(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - /* Reports userspace that an unknown keycode were got */ - return KEY_RESERVED; + ir_dev->props->close(ir_dev->props->priv); } -EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); /** - * ir_input_register() - sets the IR keycode table and add the handlers + * __ir_input_register() - sets the IR keycode table and add the handlers * for keymap table get/set * @input_dev: the struct input_dev descriptor of the device * @rc_tab: the struct ir_scancode_table table of scancode/keymap @@ -402,13 +426,13 @@ EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); * It will register the input/evdev interface for the device and * register the syfs code for IR class */ -int ir_input_register(struct input_dev *input_dev, +int __ir_input_register(struct input_dev *input_dev, const struct ir_scancode_table *rc_tab, - const struct ir_dev_props *props) + const struct ir_dev_props *props, + const char *driver_name) { struct ir_input_dev *ir_dev; - struct ir_scancode *keymap = rc_tab->scan; - int i, rc; + int rc; if (rc_tab->scan == NULL || !rc_tab->size) return -EINVAL; @@ -417,57 +441,77 @@ int ir_input_register(struct input_dev *input_dev, if (!ir_dev) return -ENOMEM; - spin_lock_init(&ir_dev->rc_tab.lock); - - ir_dev->rc_tab.size = ir_roundup_tablesize(rc_tab->size); - ir_dev->rc_tab.scan = kzalloc(ir_dev->rc_tab.size * - sizeof(struct ir_scancode), GFP_KERNEL); - if (!ir_dev->rc_tab.scan) { - kfree(ir_dev); - return -ENOMEM; + ir_dev->driver_name = kasprintf(GFP_KERNEL, "%s", driver_name); + if (!ir_dev->driver_name) { + rc = -ENOMEM; + goto out_dev; } - IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n", - ir_dev->rc_tab.size, - ir_dev->rc_tab.size * sizeof(ir_dev->rc_tab.scan)); + input_dev->getkeycode = ir_getkeycode; + input_dev->setkeycode = ir_setkeycode; + input_set_drvdata(input_dev, ir_dev); + ir_dev->input_dev = input_dev; - ir_copy_table(&ir_dev->rc_tab, rc_tab); - ir_dev->props = props; + spin_lock_init(&ir_dev->rc_tab.lock); + spin_lock_init(&ir_dev->keylock); + setup_timer(&ir_dev->timer_keyup, ir_timer_keyup, (unsigned long)ir_dev); + + ir_dev->rc_tab.name = rc_tab->name; + ir_dev->rc_tab.ir_type = rc_tab->ir_type; + ir_dev->rc_tab.alloc = roundup_pow_of_two(rc_tab->size * + sizeof(struct ir_scancode)); + ir_dev->rc_tab.scan = kmalloc(ir_dev->rc_tab.alloc, GFP_KERNEL); + ir_dev->rc_tab.size = ir_dev->rc_tab.alloc / sizeof(struct ir_scancode); + if (props) { + ir_dev->props = props; + if (props->open) + input_dev->open = ir_open; + if (props->close) + input_dev->close = ir_close; + } - /* set the bits for the keys */ - IR_dprintk(1, "key map size: %d\n", rc_tab->size); - for (i = 0; i < rc_tab->size; i++) { - IR_dprintk(1, "#%d: setting bit for keycode 0x%04x\n", - i, keymap[i].keycode); - set_bit(keymap[i].keycode, input_dev->keybit); + if (!ir_dev->rc_tab.scan) { + rc = -ENOMEM; + goto out_name; } - clear_bit(0, input_dev->keybit); + + IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n", + ir_dev->rc_tab.size, ir_dev->rc_tab.alloc); set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_REP, input_dev->evbit); - input_dev->getkeycode = ir_getkeycode; - input_dev->setkeycode = ir_setkeycode; - input_set_drvdata(input_dev, ir_dev); + if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) { + rc = -ENOMEM; + goto out_table; + } - rc = input_register_device(input_dev); + rc = ir_register_class(input_dev); if (rc < 0) - goto err; + goto out_table; - rc = ir_register_class(input_dev); - if (rc < 0) { - input_unregister_device(input_dev); - goto err; + if (ir_dev->props->driver_type == RC_DRIVER_IR_RAW) { + rc = ir_raw_event_register(input_dev); + if (rc < 0) + goto out_event; } + IR_dprintk(1, "Registered input device on %s for %s remote.\n", + driver_name, rc_tab->name); + return 0; -err: - kfree(rc_tab->scan); +out_event: + ir_unregister_class(input_dev); +out_table: + kfree(ir_dev->rc_tab.scan); +out_name: + kfree(ir_dev->driver_name); +out_dev: kfree(ir_dev); - input_set_drvdata(input_dev, NULL); return rc; } -EXPORT_SYMBOL_GPL(ir_input_register); +EXPORT_SYMBOL_GPL(__ir_input_register); /** * ir_input_unregister() - unregisters IR and frees resources @@ -475,9 +519,9 @@ EXPORT_SYMBOL_GPL(ir_input_register); * This routine is used to free memory and de-register interfaces. */ -void ir_input_unregister(struct input_dev *dev) +void ir_input_unregister(struct input_dev *input_dev) { - struct ir_input_dev *ir_dev = input_get_drvdata(dev); + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); struct ir_scancode_table *rc_tab; if (!ir_dev) @@ -485,15 +529,18 @@ void ir_input_unregister(struct input_dev *dev) IR_dprintk(1, "Freed keycode table\n"); + del_timer_sync(&ir_dev->timer_keyup); + if (ir_dev->props->driver_type == RC_DRIVER_IR_RAW) + ir_raw_event_unregister(input_dev); rc_tab = &ir_dev->rc_tab; rc_tab->size = 0; kfree(rc_tab->scan); rc_tab->scan = NULL; - ir_unregister_class(dev); + ir_unregister_class(input_dev); + kfree(ir_dev->driver_name); kfree(ir_dev); - input_unregister_device(dev); } EXPORT_SYMBOL_GPL(ir_input_unregister); diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c new file mode 100644 index 000000000000..ba79233112ef --- /dev/null +++ b/drivers/media/IR/ir-nec-decoder.c @@ -0,0 +1,328 @@ +/* ir-nec-decoder.c - handle NEC IR Pulse/Space protocol + * + * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + * + * 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 <linux/bitrev.h> +#include "ir-core-priv.h" + +#define NEC_NBITS 32 +#define NEC_UNIT 562500 /* ns */ +#define NEC_HEADER_PULSE (16 * NEC_UNIT) +#define NECX_HEADER_PULSE (8 * NEC_UNIT) /* Less common NEC variant */ +#define NEC_HEADER_SPACE (8 * NEC_UNIT) +#define NEC_REPEAT_SPACE (8 * NEC_UNIT) +#define NEC_BIT_PULSE (1 * NEC_UNIT) +#define NEC_BIT_0_SPACE (1 * NEC_UNIT) +#define NEC_BIT_1_SPACE (3 * NEC_UNIT) +#define NEC_TRAILER_PULSE (1 * NEC_UNIT) +#define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */ + +/* Used to register nec_decoder clients */ +static LIST_HEAD(decoder_list); +static DEFINE_SPINLOCK(decoder_lock); + +enum nec_state { + STATE_INACTIVE, + STATE_HEADER_SPACE, + STATE_BIT_PULSE, + STATE_BIT_SPACE, + STATE_TRAILER_PULSE, + STATE_TRAILER_SPACE, +}; + +struct decoder_data { + struct list_head list; + struct ir_input_dev *ir_dev; + int enabled:1; + + /* State machine control */ + enum nec_state state; + u32 nec_bits; + unsigned count; +}; + + +/** + * get_decoder_data() - gets decoder data + * @input_dev: input device + * + * Returns the struct decoder_data that corresponds to a device + */ +static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) +{ + struct decoder_data *data = NULL; + + spin_lock(&decoder_lock); + list_for_each_entry(data, &decoder_list, list) { + if (data->ir_dev == ir_dev) + break; + } + spin_unlock(&decoder_lock); + return data; +} + +static ssize_t store_enabled(struct device *d, + struct device_attribute *mattr, + const char *buf, + size_t len) +{ + unsigned long value; + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (strict_strtoul(buf, 10, &value) || value > 1) + return -EINVAL; + + data->enabled = value; + + return len; +} + +static ssize_t show_enabled(struct device *d, + struct device_attribute *mattr, char *buf) +{ + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (data->enabled) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); + +static struct attribute *decoder_attributes[] = { + &dev_attr_enabled.attr, + NULL +}; + +static struct attribute_group decoder_attribute_group = { + .name = "nec_decoder", + .attrs = decoder_attributes, +}; + +/** + * ir_nec_decode() - Decode one NEC pulse or space + * @input_dev: the struct input_dev descriptor of the device + * @duration: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) +{ + struct decoder_data *data; + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + u32 scancode; + u8 address, not_address, command, not_command; + + data = get_decoder_data(ir_dev); + if (!data) + return -EINVAL; + + if (!data->enabled) + return 0; + + if (IS_RESET(ev)) { + data->state = STATE_INACTIVE; + return 0; + } + + IR_dprintk(2, "NEC decode started at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2) && + !eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2)) + break; + + data->count = 0; + data->state = STATE_HEADER_SPACE; + return 0; + + case STATE_HEADER_SPACE: + if (ev.pulse) + break; + + if (eq_margin(ev.duration, NEC_HEADER_SPACE, NEC_UNIT / 2)) { + data->state = STATE_BIT_PULSE; + return 0; + } else if (eq_margin(ev.duration, NEC_REPEAT_SPACE, NEC_UNIT / 2)) { + ir_repeat(input_dev); + IR_dprintk(1, "Repeat last key\n"); + data->state = STATE_TRAILER_PULSE; + return 0; + } + + break; + + case STATE_BIT_PULSE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, NEC_BIT_PULSE, NEC_UNIT / 2)) + break; + + data->state = STATE_BIT_SPACE; + return 0; + + case STATE_BIT_SPACE: + if (ev.pulse) + break; + + data->nec_bits <<= 1; + if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2)) + data->nec_bits |= 1; + else if (!eq_margin(ev.duration, NEC_BIT_0_SPACE, NEC_UNIT / 2)) + break; + data->count++; + + if (data->count == NEC_NBITS) + data->state = STATE_TRAILER_PULSE; + else + data->state = STATE_BIT_PULSE; + + return 0; + + case STATE_TRAILER_PULSE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, NEC_TRAILER_PULSE, NEC_UNIT / 2)) + break; + + data->state = STATE_TRAILER_SPACE; + return 0; + + case STATE_TRAILER_SPACE: + if (ev.pulse) + break; + + if (!geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2)) + break; + + address = bitrev8((data->nec_bits >> 24) & 0xff); + not_address = bitrev8((data->nec_bits >> 16) & 0xff); + command = bitrev8((data->nec_bits >> 8) & 0xff); + not_command = bitrev8((data->nec_bits >> 0) & 0xff); + + if ((command ^ not_command) != 0xff) { + IR_dprintk(1, "NEC checksum error: received 0x%08x\n", + data->nec_bits); + break; + } + + if ((address ^ not_address) != 0xff) { + /* Extended NEC */ + scancode = address << 16 | + not_address << 8 | + command; + IR_dprintk(1, "NEC (Ext) scancode 0x%06x\n", scancode); + } else { + /* Normal NEC */ + scancode = address << 8 | command; + IR_dprintk(1, "NEC scancode 0x%04x\n", scancode); + } + + ir_keydown(input_dev, scancode, 0); + data->state = STATE_INACTIVE; + return 0; + } + + IR_dprintk(1, "NEC decode failed at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static int ir_nec_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct decoder_data *data; + int rc; + + rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); + if (rc < 0) + return rc; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + return -ENOMEM; + } + + data->ir_dev = ir_dev; + data->enabled = 1; + + spin_lock(&decoder_lock); + list_add_tail(&data->list, &decoder_list); + spin_unlock(&decoder_lock); + + return 0; +} + +static int ir_nec_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + static struct decoder_data *data; + + data = get_decoder_data(ir_dev); + if (!data) + return 0; + + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + + spin_lock(&decoder_lock); + list_del(&data->list); + spin_unlock(&decoder_lock); + + return 0; +} + +static struct ir_raw_handler nec_handler = { + .decode = ir_nec_decode, + .raw_register = ir_nec_register, + .raw_unregister = ir_nec_unregister, +}; + +static int __init ir_nec_decode_init(void) +{ + ir_raw_handler_register(&nec_handler); + + printk(KERN_INFO "IR NEC protocol handler initialized\n"); + return 0; +} + +static void __exit ir_nec_decode_exit(void) +{ + ir_raw_handler_unregister(&nec_handler); +} + +module_init(ir_nec_decode_init); +module_exit(ir_nec_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); +MODULE_DESCRIPTION("NEC IR protocol decoder"); diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c new file mode 100644 index 000000000000..ea68a3f2effa --- /dev/null +++ b/drivers/media/IR/ir-raw-event.c @@ -0,0 +1,251 @@ +/* ir-raw-event.c - handle IR Pulse/Space event + * + * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + * + * 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 <linux/workqueue.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include "ir-core-priv.h" + +/* Define the max number of pulse/space transitions to buffer */ +#define MAX_IR_EVENT_SIZE 512 + +/* Used to handle IR raw handler extensions */ +static LIST_HEAD(ir_raw_handler_list); +static DEFINE_SPINLOCK(ir_raw_handler_lock); + +/** + * RUN_DECODER() - runs an operation on all IR decoders + * @ops: IR raw handler operation to be called + * @arg: arguments to be passed to the callback + * + * Calls ir_raw_handler::ops for all registered IR handlers. It prevents + * new decode addition/removal while running, by locking ir_raw_handler_lock + * mutex. If an error occurs, it stops the ops. Otherwise, it returns a sum + * of the return codes. + */ +#define RUN_DECODER(ops, ...) ({ \ + struct ir_raw_handler *_ir_raw_handler; \ + int _sumrc = 0, _rc; \ + spin_lock(&ir_raw_handler_lock); \ + list_for_each_entry(_ir_raw_handler, &ir_raw_handler_list, list) { \ + if (_ir_raw_handler->ops) { \ + _rc = _ir_raw_handler->ops(__VA_ARGS__); \ + if (_rc < 0) \ + break; \ + _sumrc += _rc; \ + } \ + } \ + spin_unlock(&ir_raw_handler_lock); \ + _sumrc; \ +}) + +#ifdef MODULE +/* Used to load the decoders */ +static struct work_struct wq_load; +#endif + +static void ir_raw_event_work(struct work_struct *work) +{ + struct ir_raw_event ev; + struct ir_raw_event_ctrl *raw = + container_of(work, struct ir_raw_event_ctrl, rx_work); + + while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) + RUN_DECODER(decode, raw->input_dev, ev); +} + +int ir_raw_event_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + int rc; + + ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL); + if (!ir->raw) + return -ENOMEM; + + ir->raw->input_dev = input_dev; + INIT_WORK(&ir->raw->rx_work, ir_raw_event_work); + + rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE, + GFP_KERNEL); + if (rc < 0) { + kfree(ir->raw); + ir->raw = NULL; + return rc; + } + + rc = RUN_DECODER(raw_register, input_dev); + if (rc < 0) { + kfifo_free(&ir->raw->kfifo); + kfree(ir->raw); + ir->raw = NULL; + return rc; + } + + return rc; +} + +void ir_raw_event_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + + if (!ir->raw) + return; + + cancel_work_sync(&ir->raw->rx_work); + RUN_DECODER(raw_unregister, input_dev); + + kfifo_free(&ir->raw->kfifo); + kfree(ir->raw); + ir->raw = NULL; +} + +/** + * ir_raw_event_store() - pass a pulse/space duration to the raw ir decoders + * @input_dev: the struct input_dev device descriptor + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This routine (which may be called from an interrupt context) stores a + * pulse/space duration for the raw ir decoding state machines. Pulses are + * signalled as positive values and spaces as negative values. A zero value + * will reset the decoding state machines. + */ +int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + + if (!ir->raw) + return -EINVAL; + + if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev)) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(ir_raw_event_store); + +/** + * ir_raw_event_store_edge() - notify raw ir decoders of the start of a pulse/space + * @input_dev: the struct input_dev device descriptor + * @type: the type of the event that has occurred + * + * This routine (which may be called from an interrupt context) is used to + * store the beginning of an ir pulse or space (or the start/end of ir + * reception) for the raw ir decoding state machines. This is used by + * hardware which does not provide durations directly but only interrupts + * (or similar events) on state change. + */ +int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type type) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + ktime_t now; + s64 delta; /* ns */ + struct ir_raw_event ev; + int rc = 0; + + if (!ir->raw) + return -EINVAL; + + now = ktime_get(); + delta = ktime_to_ns(ktime_sub(now, ir->raw->last_event)); + + /* Check for a long duration since last event or if we're + * being called for the first time, note that delta can't + * possibly be negative. + */ + ev.duration = 0; + if (delta > IR_MAX_DURATION || !ir->raw->last_type) + type |= IR_START_EVENT; + else + ev.duration = delta; + + if (type & IR_START_EVENT) + ir_raw_event_reset(input_dev); + else if (ir->raw->last_type & IR_SPACE) { + ev.pulse = false; + rc = ir_raw_event_store(input_dev, &ev); + } else if (ir->raw->last_type & IR_PULSE) { + ev.pulse = true; + rc = ir_raw_event_store(input_dev, &ev); + } else + return 0; + + ir->raw->last_event = now; + ir->raw->last_type = type; + return rc; +} +EXPORT_SYMBOL_GPL(ir_raw_event_store_edge); + +/** + * ir_raw_event_handle() - schedules the decoding of stored ir data + * @input_dev: the struct input_dev device descriptor + * + * This routine will signal the workqueue to start decoding stored ir data. + */ +void ir_raw_event_handle(struct input_dev *input_dev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + + if (!ir->raw) + return; + + schedule_work(&ir->raw->rx_work); +} +EXPORT_SYMBOL_GPL(ir_raw_event_handle); + +/* + * Extension interface - used to register the IR decoders + */ + +int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) +{ + spin_lock(&ir_raw_handler_lock); + list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list); + spin_unlock(&ir_raw_handler_lock); + return 0; +} +EXPORT_SYMBOL(ir_raw_handler_register); + +void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) +{ + spin_lock(&ir_raw_handler_lock); + list_del(&ir_raw_handler->list); + spin_unlock(&ir_raw_handler_lock); +} +EXPORT_SYMBOL(ir_raw_handler_unregister); + +#ifdef MODULE +static void init_decoders(struct work_struct *work) +{ + /* Load the decoder modules */ + + load_nec_decode(); + load_rc5_decode(); + load_rc6_decode(); + load_jvc_decode(); + load_sony_decode(); + + /* If needed, we may later add some init code. In this case, + it is needed to change the CONFIG_MODULE test at ir-core.h + */ +} +#endif + +void ir_raw_init(void) +{ +#ifdef MODULE + INIT_WORK(&wq_load, init_decoders); + schedule_work(&wq_load); +#endif +} diff --git a/drivers/media/IR/ir-rc5-decoder.c b/drivers/media/IR/ir-rc5-decoder.c new file mode 100644 index 000000000000..23cdb1b1a3bc --- /dev/null +++ b/drivers/media/IR/ir-rc5-decoder.c @@ -0,0 +1,324 @@ +/* ir-rc5-decoder.c - handle RC5(x) IR Pulse/Space protocol + * + * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + * + * 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 code handles 14 bits RC5 protocols and 20 bits RC5x protocols. + * There are other variants that use a different number of bits. + * This is currently unsupported. + * It considers a carrier of 36 kHz, with a total of 14/20 bits, where + * the first two bits are start bits, and a third one is a filing bit + */ + +#include "ir-core-priv.h" + +#define RC5_NBITS 14 +#define RC5X_NBITS 20 +#define CHECK_RC5X_NBITS 8 +#define RC5_UNIT 888888 /* ns */ +#define RC5_BIT_START (1 * RC5_UNIT) +#define RC5_BIT_END (1 * RC5_UNIT) +#define RC5X_SPACE (4 * RC5_UNIT) + +/* Used to register rc5_decoder clients */ +static LIST_HEAD(decoder_list); +static DEFINE_SPINLOCK(decoder_lock); + +enum rc5_state { + STATE_INACTIVE, + STATE_BIT_START, + STATE_BIT_END, + STATE_CHECK_RC5X, + STATE_FINISHED, +}; + +struct decoder_data { + struct list_head list; + struct ir_input_dev *ir_dev; + int enabled:1; + + /* State machine control */ + enum rc5_state state; + u32 rc5_bits; + struct ir_raw_event prev_ev; + unsigned count; + unsigned wanted_bits; +}; + + +/** + * get_decoder_data() - gets decoder data + * @input_dev: input device + * + * Returns the struct decoder_data that corresponds to a device + */ + +static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) +{ + struct decoder_data *data = NULL; + + spin_lock(&decoder_lock); + list_for_each_entry(data, &decoder_list, list) { + if (data->ir_dev == ir_dev) + break; + } + spin_unlock(&decoder_lock); + return data; +} + +static ssize_t store_enabled(struct device *d, + struct device_attribute *mattr, + const char *buf, + size_t len) +{ + unsigned long value; + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (strict_strtoul(buf, 10, &value) || value > 1) + return -EINVAL; + + data->enabled = value; + + return len; +} + +static ssize_t show_enabled(struct device *d, + struct device_attribute *mattr, char *buf) +{ + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (data->enabled) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); + +static struct attribute *decoder_attributes[] = { + &dev_attr_enabled.attr, + NULL +}; + +static struct attribute_group decoder_attribute_group = { + .name = "rc5_decoder", + .attrs = decoder_attributes, +}; + +/** + * ir_rc5_decode() - Decode one RC-5 pulse or space + * @input_dev: the struct input_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_rc5_decode(struct input_dev *input_dev, struct ir_raw_event ev) +{ + struct decoder_data *data; + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + u8 toggle; + u32 scancode; + + data = get_decoder_data(ir_dev); + if (!data) + return -EINVAL; + + if (!data->enabled) + return 0; + + if (IS_RESET(ev)) { + data->state = STATE_INACTIVE; + return 0; + } + + if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) + goto out; + +again: + IR_dprintk(2, "RC5(x) decode started at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + + if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) + return 0; + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + data->state = STATE_BIT_START; + data->count = 1; + /* We just need enough bits to get to STATE_CHECK_RC5X */ + data->wanted_bits = RC5X_NBITS; + decrease_duration(&ev, RC5_BIT_START); + goto again; + + case STATE_BIT_START: + if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2)) + break; + + data->rc5_bits <<= 1; + if (!ev.pulse) + data->rc5_bits |= 1; + data->count++; + data->prev_ev = ev; + data->state = STATE_BIT_END; + return 0; + + case STATE_BIT_END: + if (!is_transition(&ev, &data->prev_ev)) + break; + + if (data->count == data->wanted_bits) + data->state = STATE_FINISHED; + else if (data->count == CHECK_RC5X_NBITS) + data->state = STATE_CHECK_RC5X; + else + data->state = STATE_BIT_START; + + decrease_duration(&ev, RC5_BIT_END); + goto again; + + case STATE_CHECK_RC5X: + if (!ev.pulse && geq_margin(ev.duration, RC5X_SPACE, RC5_UNIT / 2)) { + /* RC5X */ + data->wanted_bits = RC5X_NBITS; + decrease_duration(&ev, RC5X_SPACE); + } else { + /* RC5 */ + data->wanted_bits = RC5_NBITS; + } + data->state = STATE_BIT_START; + goto again; + + case STATE_FINISHED: + if (ev.pulse) + break; + + if (data->wanted_bits == RC5X_NBITS) { + /* RC5X */ + u8 xdata, command, system; + xdata = (data->rc5_bits & 0x0003F) >> 0; + command = (data->rc5_bits & 0x00FC0) >> 6; + system = (data->rc5_bits & 0x1F000) >> 12; + toggle = (data->rc5_bits & 0x20000) ? 1 : 0; + command += (data->rc5_bits & 0x01000) ? 0 : 0x40; + scancode = system << 16 | command << 8 | xdata; + + IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n", + scancode, toggle); + + } else { + /* RC5 */ + u8 command, system; + command = (data->rc5_bits & 0x0003F) >> 0; + system = (data->rc5_bits & 0x007C0) >> 6; + toggle = (data->rc5_bits & 0x00800) ? 1 : 0; + command += (data->rc5_bits & 0x01000) ? 0 : 0x40; + scancode = system << 8 | command; + + IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n", + scancode, toggle); + } + + ir_keydown(input_dev, scancode, toggle); + data->state = STATE_INACTIVE; + return 0; + } + +out: + IR_dprintk(1, "RC5(x) decode failed at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static int ir_rc5_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct decoder_data *data; + int rc; + + rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); + if (rc < 0) + return rc; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + return -ENOMEM; + } + + data->ir_dev = ir_dev; + data->enabled = 1; + + spin_lock(&decoder_lock); + list_add_tail(&data->list, &decoder_list); + spin_unlock(&decoder_lock); + + return 0; +} + +static int ir_rc5_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + static struct decoder_data *data; + + data = get_decoder_data(ir_dev); + if (!data) + return 0; + + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + + spin_lock(&decoder_lock); + list_del(&data->list); + spin_unlock(&decoder_lock); + + return 0; +} + +static struct ir_raw_handler rc5_handler = { + .decode = ir_rc5_decode, + .raw_register = ir_rc5_register, + .raw_unregister = ir_rc5_unregister, +}; + +static int __init ir_rc5_decode_init(void) +{ + ir_raw_handler_register(&rc5_handler); + + printk(KERN_INFO "IR RC5(x) protocol handler initialized\n"); + return 0; +} + +static void __exit ir_rc5_decode_exit(void) +{ + ir_raw_handler_unregister(&rc5_handler); +} + +module_init(ir_rc5_decode_init); +module_exit(ir_rc5_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); +MODULE_DESCRIPTION("RC5(x) IR protocol decoder"); diff --git a/drivers/media/IR/ir-rc6-decoder.c b/drivers/media/IR/ir-rc6-decoder.c new file mode 100644 index 000000000000..2bf479f4f1bc --- /dev/null +++ b/drivers/media/IR/ir-rc6-decoder.c @@ -0,0 +1,419 @@ +/* ir-rc6-decoder.c - A decoder for the RC6 IR protocol + * + * Copyright (C) 2010 by David Härdeman <david@hardeman.nu> + * + * 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. + * + * 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 "ir-core-priv.h" + +/* + * This decoder currently supports: + * RC6-0-16 (standard toggle bit in header) + * RC6-6A-24 (no toggle bit) + * RC6-6A-32 (MCE version with toggle bit in body) + */ + +#define RC6_UNIT 444444 /* us */ +#define RC6_HEADER_NBITS 4 /* not including toggle bit */ +#define RC6_0_NBITS 16 +#define RC6_6A_SMALL_NBITS 24 +#define RC6_6A_LARGE_NBITS 32 +#define RC6_PREFIX_PULSE (6 * RC6_UNIT) +#define RC6_PREFIX_SPACE (2 * RC6_UNIT) +#define RC6_BIT_START (1 * RC6_UNIT) +#define RC6_BIT_END (1 * RC6_UNIT) +#define RC6_TOGGLE_START (2 * RC6_UNIT) +#define RC6_TOGGLE_END (2 * RC6_UNIT) +#define RC6_MODE_MASK 0x07 /* for the header bits */ +#define RC6_STARTBIT_MASK 0x08 /* for the header bits */ +#define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ + +/* Used to register rc6_decoder clients */ +static LIST_HEAD(decoder_list); +static DEFINE_SPINLOCK(decoder_lock); + +enum rc6_mode { + RC6_MODE_0, + RC6_MODE_6A, + RC6_MODE_UNKNOWN, +}; + +enum rc6_state { + STATE_INACTIVE, + STATE_PREFIX_SPACE, + STATE_HEADER_BIT_START, + STATE_HEADER_BIT_END, + STATE_TOGGLE_START, + STATE_TOGGLE_END, + STATE_BODY_BIT_START, + STATE_BODY_BIT_END, + STATE_FINISHED, +}; + +struct decoder_data { + struct list_head list; + struct ir_input_dev *ir_dev; + int enabled:1; + + /* State machine control */ + enum rc6_state state; + u8 header; + u32 body; + struct ir_raw_event prev_ev; + bool toggle; + unsigned count; + unsigned wanted_bits; +}; + + +/** + * get_decoder_data() - gets decoder data + * @input_dev: input device + * + * Returns the struct decoder_data that corresponds to a device + */ +static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) +{ + struct decoder_data *data = NULL; + + spin_lock(&decoder_lock); + list_for_each_entry(data, &decoder_list, list) { + if (data->ir_dev == ir_dev) + break; + } + spin_unlock(&decoder_lock); + return data; +} + +static ssize_t store_enabled(struct device *d, + struct device_attribute *mattr, + const char *buf, + size_t len) +{ + unsigned long value; + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (strict_strtoul(buf, 10, &value) || value > 1) + return -EINVAL; + + data->enabled = value; + + return len; +} + +static ssize_t show_enabled(struct device *d, + struct device_attribute *mattr, char *buf) +{ + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (data->enabled) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); + +static struct attribute *decoder_attributes[] = { + &dev_attr_enabled.attr, + NULL +}; + +static struct attribute_group decoder_attribute_group = { + .name = "rc6_decoder", + .attrs = decoder_attributes, +}; + +static enum rc6_mode rc6_mode(struct decoder_data *data) { + switch (data->header & RC6_MODE_MASK) { + case 0: + return RC6_MODE_0; + case 6: + if (!data->toggle) + return RC6_MODE_6A; + /* fall through */ + default: + return RC6_MODE_UNKNOWN; + } +} + +/** + * ir_rc6_decode() - Decode one RC6 pulse or space + * @input_dev: the struct input_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_rc6_decode(struct input_dev *input_dev, struct ir_raw_event ev) +{ + struct decoder_data *data; + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + u32 scancode; + u8 toggle; + + data = get_decoder_data(ir_dev); + if (!data) + return -EINVAL; + + if (!data->enabled) + return 0; + + if (IS_RESET(ev)) { + data->state = STATE_INACTIVE; + return 0; + } + + if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) + goto out; + +again: + IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + + if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) + return 0; + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + /* Note: larger margin on first pulse since each RC6_UNIT + is quite short and some hardware takes some time to + adjust to the signal */ + if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT)) + break; + + data->state = STATE_PREFIX_SPACE; + data->count = 0; + return 0; + + case STATE_PREFIX_SPACE: + if (ev.pulse) + break; + + if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2)) + break; + + data->state = STATE_HEADER_BIT_START; + return 0; + + case STATE_HEADER_BIT_START: + if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) + break; + + data->header <<= 1; + if (ev.pulse) + data->header |= 1; + data->count++; + data->prev_ev = ev; + data->state = STATE_HEADER_BIT_END; + return 0; + + case STATE_HEADER_BIT_END: + if (!is_transition(&ev, &data->prev_ev)) + break; + + if (data->count == RC6_HEADER_NBITS) + data->state = STATE_TOGGLE_START; + else + data->state = STATE_HEADER_BIT_START; + + decrease_duration(&ev, RC6_BIT_END); + goto again; + + case STATE_TOGGLE_START: + if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2)) + break; + + data->toggle = ev.pulse; + data->prev_ev = ev; + data->state = STATE_TOGGLE_END; + return 0; + + case STATE_TOGGLE_END: + if (!is_transition(&ev, &data->prev_ev) || + !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) + break; + + if (!(data->header & RC6_STARTBIT_MASK)) { + IR_dprintk(1, "RC6 invalid start bit\n"); + break; + } + + data->state = STATE_BODY_BIT_START; + data->prev_ev = ev; + decrease_duration(&ev, RC6_TOGGLE_END); + data->count = 0; + + switch (rc6_mode(data)) { + case RC6_MODE_0: + data->wanted_bits = RC6_0_NBITS; + break; + case RC6_MODE_6A: + /* This might look weird, but we basically + check the value of the first body bit to + determine the number of bits in mode 6A */ + if ((!ev.pulse && !geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) || + geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) + data->wanted_bits = RC6_6A_LARGE_NBITS; + else + data->wanted_bits = RC6_6A_SMALL_NBITS; + break; + default: + IR_dprintk(1, "RC6 unknown mode\n"); + goto out; + } + goto again; + + case STATE_BODY_BIT_START: + if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) + break; + + data->body <<= 1; + if (ev.pulse) + data->body |= 1; + data->count++; + data->prev_ev = ev; + + data->state = STATE_BODY_BIT_END; + return 0; + + case STATE_BODY_BIT_END: + if (!is_transition(&ev, &data->prev_ev)) + break; + + if (data->count == data->wanted_bits) + data->state = STATE_FINISHED; + else + data->state = STATE_BODY_BIT_START; + + decrease_duration(&ev, RC6_BIT_END); + goto again; + + case STATE_FINISHED: + if (ev.pulse) + break; + + switch (rc6_mode(data)) { + case RC6_MODE_0: + scancode = data->body & 0xffff; + toggle = data->toggle; + IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", + scancode, toggle); + break; + case RC6_MODE_6A: + if (data->wanted_bits == RC6_6A_LARGE_NBITS) { + toggle = data->body & RC6_6A_MCE_TOGGLE_MASK ? 1 : 0; + scancode = data->body & ~RC6_6A_MCE_TOGGLE_MASK; + } else { + toggle = 0; + scancode = data->body & 0xffffff; + } + + IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n", + scancode, toggle); + break; + default: + IR_dprintk(1, "RC6 unknown mode\n"); + goto out; + } + + ir_keydown(input_dev, scancode, toggle); + data->state = STATE_INACTIVE; + return 0; + } + +out: + IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static int ir_rc6_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct decoder_data *data; + int rc; + + rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); + if (rc < 0) + return rc; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + return -ENOMEM; + } + + data->ir_dev = ir_dev; + data->enabled = 1; + + spin_lock(&decoder_lock); + list_add_tail(&data->list, &decoder_list); + spin_unlock(&decoder_lock); + + return 0; +} + +static int ir_rc6_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + static struct decoder_data *data; + + data = get_decoder_data(ir_dev); + if (!data) + return 0; + + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + + spin_lock(&decoder_lock); + list_del(&data->list); + spin_unlock(&decoder_lock); + + return 0; +} + +static struct ir_raw_handler rc6_handler = { + .decode = ir_rc6_decode, + .raw_register = ir_rc6_register, + .raw_unregister = ir_rc6_unregister, +}; + +static int __init ir_rc6_decode_init(void) +{ + ir_raw_handler_register(&rc6_handler); + + printk(KERN_INFO "IR RC6 protocol handler initialized\n"); + return 0; +} + +static void __exit ir_rc6_decode_exit(void) +{ + ir_raw_handler_unregister(&rc6_handler); +} + +module_init(ir_rc6_decode_init); +module_exit(ir_rc6_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); +MODULE_DESCRIPTION("RC6 IR protocol decoder"); diff --git a/drivers/media/IR/ir-sony-decoder.c b/drivers/media/IR/ir-sony-decoder.c new file mode 100644 index 000000000000..9f440c5c060d --- /dev/null +++ b/drivers/media/IR/ir-sony-decoder.c @@ -0,0 +1,312 @@ +/* ir-sony-decoder.c - handle Sony IR Pulse/Space protocol + * + * Copyright (C) 2010 by David Härdeman <david@hardeman.nu> + * + * 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. + * + * 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 <linux/bitrev.h> +#include "ir-core-priv.h" + +#define SONY_UNIT 600000 /* ns */ +#define SONY_HEADER_PULSE (4 * SONY_UNIT) +#define SONY_HEADER_SPACE (1 * SONY_UNIT) +#define SONY_BIT_0_PULSE (1 * SONY_UNIT) +#define SONY_BIT_1_PULSE (2 * SONY_UNIT) +#define SONY_BIT_SPACE (1 * SONY_UNIT) +#define SONY_TRAILER_SPACE (10 * SONY_UNIT) /* minimum */ + +/* Used to register sony_decoder clients */ +static LIST_HEAD(decoder_list); +static DEFINE_SPINLOCK(decoder_lock); + +enum sony_state { + STATE_INACTIVE, + STATE_HEADER_SPACE, + STATE_BIT_PULSE, + STATE_BIT_SPACE, + STATE_FINISHED, +}; + +struct decoder_data { + struct list_head list; + struct ir_input_dev *ir_dev; + int enabled:1; + + /* State machine control */ + enum sony_state state; + u32 sony_bits; + unsigned count; +}; + + +/** + * get_decoder_data() - gets decoder data + * @input_dev: input device + * + * Returns the struct decoder_data that corresponds to a device + */ +static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) +{ + struct decoder_data *data = NULL; + + spin_lock(&decoder_lock); + list_for_each_entry(data, &decoder_list, list) { + if (data->ir_dev == ir_dev) + break; + } + spin_unlock(&decoder_lock); + return data; +} + +static ssize_t store_enabled(struct device *d, + struct device_attribute *mattr, + const char *buf, + size_t len) +{ + unsigned long value; + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (strict_strtoul(buf, 10, &value) || value > 1) + return -EINVAL; + + data->enabled = value; + + return len; +} + +static ssize_t show_enabled(struct device *d, + struct device_attribute *mattr, char *buf) +{ + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + struct decoder_data *data = get_decoder_data(ir_dev); + + if (!data) + return -EINVAL; + + if (data->enabled) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); + +static struct attribute *decoder_attributes[] = { + &dev_attr_enabled.attr, + NULL +}; + +static struct attribute_group decoder_attribute_group = { + .name = "sony_decoder", + .attrs = decoder_attributes, +}; + +/** + * ir_sony_decode() - Decode one Sony pulse or space + * @input_dev: the struct input_dev descriptor of the device + * @ev: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev) +{ + struct decoder_data *data; + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + u32 scancode; + u8 device, subdevice, function; + + data = get_decoder_data(ir_dev); + if (!data) + return -EINVAL; + + if (!data->enabled) + return 0; + + if (IS_RESET(ev)) { + data->state = STATE_INACTIVE; + return 0; + } + + if (!geq_margin(ev.duration, SONY_UNIT, SONY_UNIT / 2)) + goto out; + + IR_dprintk(2, "Sony decode started at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + if (!eq_margin(ev.duration, SONY_HEADER_PULSE, SONY_UNIT / 2)) + break; + + data->count = 0; + data->state = STATE_HEADER_SPACE; + return 0; + + case STATE_HEADER_SPACE: + if (ev.pulse) + break; + + if (!eq_margin(ev.duration, SONY_HEADER_SPACE, SONY_UNIT / 2)) + break; + + data->state = STATE_BIT_PULSE; + return 0; + + case STATE_BIT_PULSE: + if (!ev.pulse) + break; + + data->sony_bits <<= 1; + if (eq_margin(ev.duration, SONY_BIT_1_PULSE, SONY_UNIT / 2)) + data->sony_bits |= 1; + else if (!eq_margin(ev.duration, SONY_BIT_0_PULSE, SONY_UNIT / 2)) + break; + + data->count++; + data->state = STATE_BIT_SPACE; + return 0; + + case STATE_BIT_SPACE: + if (ev.pulse) + break; + + if (!geq_margin(ev.duration, SONY_BIT_SPACE, SONY_UNIT / 2)) + break; + + decrease_duration(&ev, SONY_BIT_SPACE); + + if (!geq_margin(ev.duration, SONY_UNIT, SONY_UNIT / 2)) { + data->state = STATE_BIT_PULSE; + return 0; + } + + data->state = STATE_FINISHED; + /* Fall through */ + + case STATE_FINISHED: + if (ev.pulse) + break; + + if (!geq_margin(ev.duration, SONY_TRAILER_SPACE, SONY_UNIT / 2)) + break; + + switch (data->count) { + case 12: + device = bitrev8((data->sony_bits << 3) & 0xF8); + subdevice = 0; + function = bitrev8((data->sony_bits >> 4) & 0xFE); + break; + case 15: + device = bitrev8((data->sony_bits >> 0) & 0xFF); + subdevice = 0; + function = bitrev8((data->sony_bits >> 7) & 0xFD); + break; + case 20: + device = bitrev8((data->sony_bits >> 5) & 0xF8); + subdevice = bitrev8((data->sony_bits >> 0) & 0xFF); + function = bitrev8((data->sony_bits >> 12) & 0xFE); + break; + default: + IR_dprintk(1, "Sony invalid bitcount %u\n", data->count); + goto out; + } + + scancode = device << 16 | subdevice << 8 | function; + IR_dprintk(1, "Sony(%u) scancode 0x%05x\n", data->count, scancode); + ir_keydown(input_dev, scancode, 0); + data->state = STATE_INACTIVE; + return 0; + } + +out: + IR_dprintk(1, "Sony decode failed at state %d (%uus %s)\n", + data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static int ir_sony_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct decoder_data *data; + int rc; + + rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); + if (rc < 0) + return rc; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + return -ENOMEM; + } + + data->ir_dev = ir_dev; + data->enabled = 1; + + spin_lock(&decoder_lock); + list_add_tail(&data->list, &decoder_list); + spin_unlock(&decoder_lock); + + return 0; +} + +static int ir_sony_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + static struct decoder_data *data; + + data = get_decoder_data(ir_dev); + if (!data) + return 0; + + sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); + + spin_lock(&decoder_lock); + list_del(&data->list); + spin_unlock(&decoder_lock); + + return 0; +} + +static struct ir_raw_handler sony_handler = { + .decode = ir_sony_decode, + .raw_register = ir_sony_register, + .raw_unregister = ir_sony_unregister, +}; + +static int __init ir_sony_decode_init(void) +{ + ir_raw_handler_register(&sony_handler); + + printk(KERN_INFO "IR Sony protocol handler initialized\n"); + return 0; +} + +static void __exit ir_sony_decode_exit(void) +{ + ir_raw_handler_unregister(&sony_handler); +} + +module_init(ir_sony_decode_init); +module_exit(ir_sony_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); +MODULE_DESCRIPTION("Sony IR protocol decoder"); diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index e14e6c486b52..d7da63e16c92 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -1,6 +1,6 @@ -/* ir-register.c - handle IR scancode->keycode tables +/* ir-sysfs.c - sysfs interface for RC devices (/sys/class/rc) * - * Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com> + * Copyright (C) 2009-2010 by Mauro Carvalho Chehab <mchehab@redhat.com> * * 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 @@ -15,15 +15,23 @@ #include <linux/slab.h> #include <linux/input.h> #include <linux/device.h> -#include <media/ir-core.h> +#include "ir-core-priv.h" #define IRRCV_NUM_DEVICES 256 /* bit array to represent IR sysfs device number */ static unsigned long ir_core_dev_number; -/* class for /sys/class/irrcv */ -static struct class *ir_input_class; +/* class for /sys/class/rc */ +static char *ir_devnode(struct device *dev, mode_t *mode) +{ + return kasprintf(GFP_KERNEL, "rc/%s", dev_name(dev)); +} + +static struct class ir_input_class = { + .name = "rc", + .devnode = ir_devnode, +}; /** * show_protocol() - shows the current IR protocol @@ -32,7 +40,7 @@ static struct class *ir_input_class; * @buf: a pointer to the output buffer * * This routine is a callback routine for input read the IR protocol type. - * it is trigged by reading /sys/class/irrcv/irrcv?/current_protocol. + * it is trigged by reading /sys/class/rc/rc?/current_protocol. * It returns the protocol name, as understood by the driver. */ static ssize_t show_protocol(struct device *d, @@ -48,13 +56,17 @@ static ssize_t show_protocol(struct device *d, if (ir_type == IR_TYPE_UNKNOWN) s = "Unknown"; else if (ir_type == IR_TYPE_RC5) - s = "RC-5"; - else if (ir_type == IR_TYPE_PD) - s = "Pulse/distance"; + s = "rc-5"; else if (ir_type == IR_TYPE_NEC) - s = "NEC"; + s = "nec"; + else if (ir_type == IR_TYPE_RC6) + s = "rc6"; + else if (ir_type == IR_TYPE_JVC) + s = "jvc"; + else if (ir_type == IR_TYPE_SONY) + s = "sony"; else - s = "Other"; + s = "other"; return sprintf(buf, "%s\n", s); } @@ -67,7 +79,7 @@ static ssize_t show_protocol(struct device *d, * @len: length of the input buffer * * This routine is a callback routine for changing the IR protocol type. - * it is trigged by reading /sys/class/irrcv/irrcv?/current_protocol. + * it is trigged by reading /sys/class/rc/rc?/current_protocol. * It changes the IR the protocol name, if the IR type is recognized * by the driver. * If an unknown protocol name is used, returns -EINVAL. @@ -78,23 +90,24 @@ static ssize_t store_protocol(struct device *d, size_t len) { struct ir_input_dev *ir_dev = dev_get_drvdata(d); - u64 ir_type = IR_TYPE_UNKNOWN; + u64 ir_type = 0; int rc = -EINVAL; unsigned long flags; char *buf; - buf = strsep((char **) &data, "\n"); - - if (!strcasecmp(buf, "rc-5")) - ir_type = IR_TYPE_RC5; - else if (!strcasecmp(buf, "pd")) - ir_type = IR_TYPE_PD; - else if (!strcasecmp(buf, "nec")) - ir_type = IR_TYPE_NEC; + while ((buf = strsep((char **) &data, " \n")) != NULL) { + if (!strcasecmp(buf, "rc-5") || !strcasecmp(buf, "rc5")) + ir_type |= IR_TYPE_RC5; + if (!strcasecmp(buf, "nec")) + ir_type |= IR_TYPE_NEC; + if (!strcasecmp(buf, "jvc")) + ir_type |= IR_TYPE_JVC; + if (!strcasecmp(buf, "sony")) + ir_type |= IR_TYPE_SONY; + } - if (ir_type == IR_TYPE_UNKNOWN) { - IR_dprintk(1, "Error setting protocol to %lld\n", - (long long)ir_type); + if (!ir_type) { + IR_dprintk(1, "Unknown protocol\n"); return -EINVAL; } @@ -112,25 +125,87 @@ static ssize_t store_protocol(struct device *d, ir_dev->rc_tab.ir_type = ir_type; spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags); - IR_dprintk(1, "Current protocol is %lld\n", + IR_dprintk(1, "Current protocol(s) is(are) %lld\n", (long long)ir_type); return len; } +static ssize_t show_supported_protocols(struct device *d, + struct device_attribute *mattr, char *buf) +{ + char *orgbuf = buf; + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + + /* FIXME: doesn't support multiple protocols at the same time */ + if (ir_dev->props->allowed_protos == IR_TYPE_UNKNOWN) + buf += sprintf(buf, "unknown "); + if (ir_dev->props->allowed_protos & IR_TYPE_RC5) + buf += sprintf(buf, "rc-5 "); + if (ir_dev->props->allowed_protos & IR_TYPE_NEC) + buf += sprintf(buf, "nec "); + if (buf == orgbuf) + buf += sprintf(buf, "other "); + + buf += sprintf(buf - 1, "\n"); + + return buf - orgbuf; +} + +#define ADD_HOTPLUG_VAR(fmt, val...) \ + do { \ + int err = add_uevent_var(env, fmt, val); \ + if (err) \ + return err; \ + } while (0) + +static int ir_dev_uevent(struct device *device, struct kobj_uevent_env *env) +{ + struct ir_input_dev *ir_dev = dev_get_drvdata(device); + + if (ir_dev->rc_tab.name) + ADD_HOTPLUG_VAR("NAME=%s", ir_dev->rc_tab.name); + if (ir_dev->driver_name) + ADD_HOTPLUG_VAR("DRV_NAME=%s", ir_dev->driver_name); + + return 0; +} + /* * Static device attribute struct with the sysfs attributes for IR's */ -static DEVICE_ATTR(current_protocol, S_IRUGO | S_IWUSR, +static DEVICE_ATTR(protocol, S_IRUGO | S_IWUSR, show_protocol, store_protocol); -static struct attribute *ir_dev_attrs[] = { - &dev_attr_current_protocol.attr, +static DEVICE_ATTR(supported_protocols, S_IRUGO | S_IWUSR, + show_supported_protocols, NULL); + +static struct attribute *ir_hw_dev_attrs[] = { + &dev_attr_protocol.attr, + &dev_attr_supported_protocols.attr, NULL, }; +static struct attribute_group ir_hw_dev_attr_grp = { + .attrs = ir_hw_dev_attrs, +}; + +static const struct attribute_group *ir_hw_dev_attr_groups[] = { + &ir_hw_dev_attr_grp, + NULL +}; + +static struct device_type rc_dev_type = { + .groups = ir_hw_dev_attr_groups, + .uevent = ir_dev_uevent, +}; + +static struct device_type ir_raw_dev_type = { + .uevent = ir_dev_uevent, +}; + /** - * ir_register_class() - creates the sysfs for /sys/class/irrcv/irrcv? + * ir_register_class() - creates the sysfs for /sys/class/rc/rc? * @input_dev: the struct input_dev descriptor of the device * * This routine is used to register the syfs code for IR class @@ -138,8 +213,7 @@ static struct attribute *ir_dev_attrs[] = { int ir_register_class(struct input_dev *input_dev) { int rc; - struct kobject *kobj; - + const char *path; struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); int devno = find_first_zero_bit(&ir_core_dev_number, IRRCV_NUM_DEVICES); @@ -147,19 +221,36 @@ int ir_register_class(struct input_dev *input_dev) if (unlikely(devno < 0)) return devno; - ir_dev->attr.attrs = ir_dev_attrs; - ir_dev->class_dev = device_create(ir_input_class, NULL, - input_dev->dev.devt, ir_dev, - "irrcv%d", devno); - kobj = &ir_dev->class_dev->kobj; - - printk(KERN_WARNING "Creating IR device %s\n", kobject_name(kobj)); - rc = sysfs_create_group(kobj, &ir_dev->attr); - if (unlikely(rc < 0)) { - device_destroy(ir_input_class, input_dev->dev.devt); - return -ENOMEM; + if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) + ir_dev->dev.type = &rc_dev_type; + else + ir_dev->dev.type = &ir_raw_dev_type; + + ir_dev->dev.class = &ir_input_class; + ir_dev->dev.parent = input_dev->dev.parent; + dev_set_name(&ir_dev->dev, "rc%d", devno); + dev_set_drvdata(&ir_dev->dev, ir_dev); + rc = device_register(&ir_dev->dev); + if (rc) + return rc; + + + input_dev->dev.parent = &ir_dev->dev; + rc = input_register_device(input_dev); + if (rc < 0) { + device_del(&ir_dev->dev); + return rc; } + __module_get(THIS_MODULE); + + path = kobject_get_path(&ir_dev->dev.kobj, GFP_KERNEL); + printk(KERN_INFO "%s: %s as %s\n", + dev_name(&ir_dev->dev), + input_dev->name ? input_dev->name : "Unspecified device", + path ? path : "N/A"); + kfree(path); + ir_dev->devno = devno; set_bit(devno, &ir_core_dev_number); @@ -168,7 +259,7 @@ int ir_register_class(struct input_dev *input_dev) /** * ir_unregister_class() - removes the sysfs for sysfs for - * /sys/class/irrcv/irrcv? + * /sys/class/rc/rc? * @input_dev: the struct input_dev descriptor of the device * * This routine is used to unregister the syfs code for IR class @@ -176,36 +267,35 @@ int ir_register_class(struct input_dev *input_dev) void ir_unregister_class(struct input_dev *input_dev) { struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - struct kobject *kobj; clear_bit(ir_dev->devno, &ir_core_dev_number); + input_unregister_device(input_dev); + device_del(&ir_dev->dev); - kobj = &ir_dev->class_dev->kobj; - - sysfs_remove_group(kobj, &ir_dev->attr); - device_destroy(ir_input_class, input_dev->dev.devt); - - kfree(ir_dev->attr.name); + module_put(THIS_MODULE); } /* - * Init/exit code for the module. Basically, creates/removes /sys/class/irrcv + * Init/exit code for the module. Basically, creates/removes /sys/class/rc */ static int __init ir_core_init(void) { - ir_input_class = class_create(THIS_MODULE, "irrcv"); - if (IS_ERR(ir_input_class)) { - printk(KERN_ERR "ir_core: unable to register irrcv class\n"); - return PTR_ERR(ir_input_class); + int rc = class_register(&ir_input_class); + if (rc) { + printk(KERN_ERR "ir_core: unable to register rc class\n"); + return rc; } + /* Initialize/load the decoders/keymap code that will be used */ + ir_raw_init(); + return 0; } static void __exit ir_core_exit(void) { - class_destroy(ir_input_class); + class_unregister(&ir_input_class); } module_init(ir_core_init); diff --git a/drivers/media/IR/keymaps/Kconfig b/drivers/media/IR/keymaps/Kconfig new file mode 100644 index 000000000000..14b22f58f823 --- /dev/null +++ b/drivers/media/IR/keymaps/Kconfig @@ -0,0 +1,15 @@ +config RC_MAP + tristate "Compile Remote Controller keymap modules" + depends on IR_CORE + default y + + ---help--- + This option enables the compilation of lots of Remote + Controller tables. They are short tables, but if you + don't use a remote controller, or prefer to load the + tables on userspace, you should disable it. + + The ir-keytable program, available at v4l-utils package + provide the tool and the same RC maps for load from + userspace. Its available at + http://git.linuxtv.org/v4l-utils diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile new file mode 100644 index 000000000000..ec25258a955f --- /dev/null +++ b/drivers/media/IR/keymaps/Makefile @@ -0,0 +1,67 @@ +obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ + rc-apac-viewcomp.o \ + rc-asus-pc39.o \ + rc-ati-tv-wonder-hd-600.o \ + rc-avermedia-a16d.o \ + rc-avermedia.o \ + rc-avermedia-cardbus.o \ + rc-avermedia-dvbt.o \ + rc-avermedia-m135a-rm-jx.o \ + rc-avertv-303.o \ + rc-behold.o \ + rc-behold-columbus.o \ + rc-budget-ci-old.o \ + rc-cinergy-1400.o \ + rc-cinergy.o \ + rc-dm1105-nec.o \ + rc-dntv-live-dvb-t.o \ + rc-dntv-live-dvbt-pro.o \ + rc-empty.o \ + rc-em-terratec.o \ + rc-encore-enltv2.o \ + rc-encore-enltv.o \ + rc-encore-enltv-fm53.o \ + rc-evga-indtube.o \ + rc-eztv.o \ + rc-flydvb.o \ + rc-flyvideo.o \ + rc-fusionhdtv-mce.o \ + rc-gadmei-rm008z.o \ + rc-genius-tvgo-a11mce.o \ + rc-gotview7135.o \ + rc-hauppauge-new.o \ + rc-imon-mce.o \ + rc-imon-pad.o \ + rc-iodata-bctv7e.o \ + rc-kaiomy.o \ + rc-kworld-315u.o \ + rc-kworld-plus-tv-analog.o \ + rc-manli.o \ + rc-msi-tvanywhere.o \ + rc-msi-tvanywhere-plus.o \ + rc-nebula.o \ + rc-nec-terratec-cinergy-xs.o \ + rc-norwood.o \ + rc-npgtech.o \ + rc-pctv-sedna.o \ + rc-pinnacle-color.o \ + rc-pinnacle-grey.o \ + rc-pinnacle-pctv-hd.o \ + rc-pixelview.o \ + rc-pixelview-mk12.o \ + rc-pixelview-new.o \ + rc-powercolor-real-angel.o \ + rc-proteus-2309.o \ + rc-purpletv.o \ + rc-pv951.o \ + rc-rc5-hauppauge-new.o \ + rc-rc5-tv.o \ + rc-real-audio-220-32-keys.o \ + rc-tbs-nec.o \ + rc-terratec-cinergy-xs.o \ + rc-tevii-nec.o \ + rc-tt-1500.o \ + rc-videomate-s350.o \ + rc-videomate-tv-pvr.o \ + rc-winfast.o \ + rc-winfast-usbii-deluxe.o diff --git a/drivers/media/IR/keymaps/rc-adstech-dvb-t-pci.c b/drivers/media/IR/keymaps/rc-adstech-dvb-t-pci.c new file mode 100644 index 000000000000..b17283176ecd --- /dev/null +++ b/drivers/media/IR/keymaps/rc-adstech-dvb-t-pci.c @@ -0,0 +1,89 @@ +/* adstech-dvb-t-pci.h - Keytable for adstech_dvb_t_pci Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* ADS Tech Instant TV DVB-T PCI Remote */ + +static struct ir_scancode adstech_dvb_t_pci[] = { + /* Keys 0 to 9 */ + { 0x4d, KEY_0 }, + { 0x57, KEY_1 }, + { 0x4f, KEY_2 }, + { 0x53, KEY_3 }, + { 0x56, KEY_4 }, + { 0x4e, KEY_5 }, + { 0x5e, KEY_6 }, + { 0x54, KEY_7 }, + { 0x4c, KEY_8 }, + { 0x5c, KEY_9 }, + + { 0x5b, KEY_POWER }, + { 0x5f, KEY_MUTE }, + { 0x55, KEY_GOTO }, + { 0x5d, KEY_SEARCH }, + { 0x17, KEY_EPG }, /* Guide */ + { 0x1f, KEY_MENU }, + { 0x0f, KEY_UP }, + { 0x46, KEY_DOWN }, + { 0x16, KEY_LEFT }, + { 0x1e, KEY_RIGHT }, + { 0x0e, KEY_SELECT }, /* Enter */ + { 0x5a, KEY_INFO }, + { 0x52, KEY_EXIT }, + { 0x59, KEY_PREVIOUS }, + { 0x51, KEY_NEXT }, + { 0x58, KEY_REWIND }, + { 0x50, KEY_FORWARD }, + { 0x44, KEY_PLAYPAUSE }, + { 0x07, KEY_STOP }, + { 0x1b, KEY_RECORD }, + { 0x13, KEY_TUNER }, /* Live */ + { 0x0a, KEY_A }, + { 0x12, KEY_B }, + { 0x03, KEY_PROG1 }, /* 1 */ + { 0x01, KEY_PROG2 }, /* 2 */ + { 0x00, KEY_PROG3 }, /* 3 */ + { 0x06, KEY_DVD }, + { 0x48, KEY_AUX }, /* Photo */ + { 0x40, KEY_VIDEO }, + { 0x19, KEY_AUDIO }, /* Music */ + { 0x0b, KEY_CHANNELUP }, + { 0x08, KEY_CHANNELDOWN }, + { 0x15, KEY_VOLUMEUP }, + { 0x1c, KEY_VOLUMEDOWN }, +}; + +static struct rc_keymap adstech_dvb_t_pci_map = { + .map = { + .scan = adstech_dvb_t_pci, + .size = ARRAY_SIZE(adstech_dvb_t_pci), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_ADSTECH_DVB_T_PCI, + } +}; + +static int __init init_rc_map_adstech_dvb_t_pci(void) +{ + return ir_register_map(&adstech_dvb_t_pci_map); +} + +static void __exit exit_rc_map_adstech_dvb_t_pci(void) +{ + ir_unregister_map(&adstech_dvb_t_pci_map); +} + +module_init(init_rc_map_adstech_dvb_t_pci) +module_exit(exit_rc_map_adstech_dvb_t_pci) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-apac-viewcomp.c b/drivers/media/IR/keymaps/rc-apac-viewcomp.c new file mode 100644 index 000000000000..0ef2b562baf0 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-apac-viewcomp.c @@ -0,0 +1,80 @@ +/* apac-viewcomp.h - Keytable for apac_viewcomp Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Attila Kondoros <attila.kondoros@chello.hu> */ + +static struct ir_scancode apac_viewcomp[] = { + + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x00, KEY_0 }, + { 0x17, KEY_LAST }, /* +100 */ + { 0x0a, KEY_LIST }, /* recall */ + + + { 0x1c, KEY_TUNER }, /* TV/FM */ + { 0x15, KEY_SEARCH }, /* scan */ + { 0x12, KEY_POWER }, /* power */ + { 0x1f, KEY_VOLUMEDOWN }, /* vol up */ + { 0x1b, KEY_VOLUMEUP }, /* vol down */ + { 0x1e, KEY_CHANNELDOWN }, /* chn up */ + { 0x1a, KEY_CHANNELUP }, /* chn down */ + + { 0x11, KEY_VIDEO }, /* video */ + { 0x0f, KEY_ZOOM }, /* full screen */ + { 0x13, KEY_MUTE }, /* mute/unmute */ + { 0x10, KEY_TEXT }, /* min */ + + { 0x0d, KEY_STOP }, /* freeze */ + { 0x0e, KEY_RECORD }, /* record */ + { 0x1d, KEY_PLAYPAUSE }, /* stop */ + { 0x19, KEY_PLAY }, /* play */ + + { 0x16, KEY_GOTO }, /* osd */ + { 0x14, KEY_REFRESH }, /* default */ + { 0x0c, KEY_KPPLUS }, /* fine tune >>>> */ + { 0x18, KEY_KPMINUS }, /* fine tune <<<< */ +}; + +static struct rc_keymap apac_viewcomp_map = { + .map = { + .scan = apac_viewcomp, + .size = ARRAY_SIZE(apac_viewcomp), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_APAC_VIEWCOMP, + } +}; + +static int __init init_rc_map_apac_viewcomp(void) +{ + return ir_register_map(&apac_viewcomp_map); +} + +static void __exit exit_rc_map_apac_viewcomp(void) +{ + ir_unregister_map(&apac_viewcomp_map); +} + +module_init(init_rc_map_apac_viewcomp) +module_exit(exit_rc_map_apac_viewcomp) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-asus-pc39.c b/drivers/media/IR/keymaps/rc-asus-pc39.c new file mode 100644 index 000000000000..2aa068cd6c75 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-asus-pc39.c @@ -0,0 +1,91 @@ +/* asus-pc39.h - Keytable for asus_pc39 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + * Marc Fargas <telenieko@telenieko.com> + * this is the remote control that comes with the asus p7131 + * which has a label saying is "Model PC-39" + */ + +static struct ir_scancode asus_pc39[] = { + /* Keys 0 to 9 */ + { 0x15, KEY_0 }, + { 0x29, KEY_1 }, + { 0x2d, KEY_2 }, + { 0x2b, KEY_3 }, + { 0x09, KEY_4 }, + { 0x0d, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x31, KEY_7 }, + { 0x35, KEY_8 }, + { 0x33, KEY_9 }, + + { 0x3e, KEY_RADIO }, /* radio */ + { 0x03, KEY_MENU }, /* dvd/menu */ + { 0x2a, KEY_VOLUMEUP }, + { 0x19, KEY_VOLUMEDOWN }, + { 0x37, KEY_UP }, + { 0x3b, KEY_DOWN }, + { 0x27, KEY_LEFT }, + { 0x2f, KEY_RIGHT }, + { 0x25, KEY_VIDEO }, /* video */ + { 0x39, KEY_AUDIO }, /* music */ + + { 0x21, KEY_TV }, /* tv */ + { 0x1d, KEY_EXIT }, /* back */ + { 0x0a, KEY_CHANNELUP }, /* channel / program + */ + { 0x1b, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x1a, KEY_ENTER }, /* enter */ + + { 0x06, KEY_PAUSE }, /* play/pause */ + { 0x1e, KEY_PREVIOUS }, /* rew */ + { 0x26, KEY_NEXT }, /* forward */ + { 0x0e, KEY_REWIND }, /* backward << */ + { 0x3a, KEY_FASTFORWARD }, /* forward >> */ + { 0x36, KEY_STOP }, + { 0x2e, KEY_RECORD }, /* recording */ + { 0x16, KEY_POWER }, /* the button that reads "close" */ + + { 0x11, KEY_ZOOM }, /* full screen */ + { 0x13, KEY_MACRO }, /* recall */ + { 0x23, KEY_HOME }, /* home */ + { 0x05, KEY_PVR }, /* picture */ + { 0x3d, KEY_MUTE }, /* mute */ + { 0x01, KEY_DVD }, /* dvd */ +}; + +static struct rc_keymap asus_pc39_map = { + .map = { + .scan = asus_pc39, + .size = ARRAY_SIZE(asus_pc39), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_ASUS_PC39, + } +}; + +static int __init init_rc_map_asus_pc39(void) +{ + return ir_register_map(&asus_pc39_map); +} + +static void __exit exit_rc_map_asus_pc39(void) +{ + ir_unregister_map(&asus_pc39_map); +} + +module_init(init_rc_map_asus_pc39) +module_exit(exit_rc_map_asus_pc39) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-ati-tv-wonder-hd-600.c b/drivers/media/IR/keymaps/rc-ati-tv-wonder-hd-600.c new file mode 100644 index 000000000000..8edfd293d010 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-ati-tv-wonder-hd-600.c @@ -0,0 +1,69 @@ +/* ati-tv-wonder-hd-600.h - Keytable for ati_tv_wonder_hd_600 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* ATI TV Wonder HD 600 USB + Devin Heitmueller <devin.heitmueller@gmail.com> + */ + +static struct ir_scancode ati_tv_wonder_hd_600[] = { + { 0x00, KEY_RECORD}, /* Row 1 */ + { 0x01, KEY_PLAYPAUSE}, + { 0x02, KEY_STOP}, + { 0x03, KEY_POWER}, + { 0x04, KEY_PREVIOUS}, /* Row 2 */ + { 0x05, KEY_REWIND}, + { 0x06, KEY_FORWARD}, + { 0x07, KEY_NEXT}, + { 0x08, KEY_EPG}, /* Row 3 */ + { 0x09, KEY_HOME}, + { 0x0a, KEY_MENU}, + { 0x0b, KEY_CHANNELUP}, + { 0x0c, KEY_BACK}, /* Row 4 */ + { 0x0d, KEY_UP}, + { 0x0e, KEY_INFO}, + { 0x0f, KEY_CHANNELDOWN}, + { 0x10, KEY_LEFT}, /* Row 5 */ + { 0x11, KEY_SELECT}, + { 0x12, KEY_RIGHT}, + { 0x13, KEY_VOLUMEUP}, + { 0x14, KEY_LAST}, /* Row 6 */ + { 0x15, KEY_DOWN}, + { 0x16, KEY_MUTE}, + { 0x17, KEY_VOLUMEDOWN}, +}; + +static struct rc_keymap ati_tv_wonder_hd_600_map = { + .map = { + .scan = ati_tv_wonder_hd_600, + .size = ARRAY_SIZE(ati_tv_wonder_hd_600), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_ATI_TV_WONDER_HD_600, + } +}; + +static int __init init_rc_map_ati_tv_wonder_hd_600(void) +{ + return ir_register_map(&ati_tv_wonder_hd_600_map); +} + +static void __exit exit_rc_map_ati_tv_wonder_hd_600(void) +{ + ir_unregister_map(&ati_tv_wonder_hd_600_map); +} + +module_init(init_rc_map_ati_tv_wonder_hd_600) +module_exit(exit_rc_map_ati_tv_wonder_hd_600) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-avermedia-a16d.c b/drivers/media/IR/keymaps/rc-avermedia-a16d.c new file mode 100644 index 000000000000..12f043587f2e --- /dev/null +++ b/drivers/media/IR/keymaps/rc-avermedia-a16d.c @@ -0,0 +1,75 @@ +/* avermedia-a16d.h - Keytable for avermedia_a16d Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode avermedia_a16d[] = { + { 0x20, KEY_LIST}, + { 0x00, KEY_POWER}, + { 0x28, KEY_1}, + { 0x18, KEY_2}, + { 0x38, KEY_3}, + { 0x24, KEY_4}, + { 0x14, KEY_5}, + { 0x34, KEY_6}, + { 0x2c, KEY_7}, + { 0x1c, KEY_8}, + { 0x3c, KEY_9}, + { 0x12, KEY_SUBTITLE}, + { 0x22, KEY_0}, + { 0x32, KEY_REWIND}, + { 0x3a, KEY_SHUFFLE}, + { 0x02, KEY_PRINT}, + { 0x11, KEY_CHANNELDOWN}, + { 0x31, KEY_CHANNELUP}, + { 0x0c, KEY_ZOOM}, + { 0x1e, KEY_VOLUMEDOWN}, + { 0x3e, KEY_VOLUMEUP}, + { 0x0a, KEY_MUTE}, + { 0x04, KEY_AUDIO}, + { 0x26, KEY_RECORD}, + { 0x06, KEY_PLAY}, + { 0x36, KEY_STOP}, + { 0x16, KEY_PAUSE}, + { 0x2e, KEY_REWIND}, + { 0x0e, KEY_FASTFORWARD}, + { 0x30, KEY_TEXT}, + { 0x21, KEY_GREEN}, + { 0x01, KEY_BLUE}, + { 0x08, KEY_EPG}, + { 0x2a, KEY_MENU}, +}; + +static struct rc_keymap avermedia_a16d_map = { + .map = { + .scan = avermedia_a16d, + .size = ARRAY_SIZE(avermedia_a16d), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_AVERMEDIA_A16D, + } +}; + +static int __init init_rc_map_avermedia_a16d(void) +{ + return ir_register_map(&avermedia_a16d_map); +} + +static void __exit exit_rc_map_avermedia_a16d(void) +{ + ir_unregister_map(&avermedia_a16d_map); +} + +module_init(init_rc_map_avermedia_a16d) +module_exit(exit_rc_map_avermedia_a16d) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-avermedia-cardbus.c b/drivers/media/IR/keymaps/rc-avermedia-cardbus.c new file mode 100644 index 000000000000..2a945b02e8ca --- /dev/null +++ b/drivers/media/IR/keymaps/rc-avermedia-cardbus.c @@ -0,0 +1,97 @@ +/* avermedia-cardbus.h - Keytable for avermedia_cardbus Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Oldrich Jedlicka <oldium.pro@seznam.cz> */ + +static struct ir_scancode avermedia_cardbus[] = { + { 0x00, KEY_POWER }, + { 0x01, KEY_TUNER }, /* TV/FM */ + { 0x03, KEY_TEXT }, /* Teletext */ + { 0x04, KEY_EPG }, + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x08, KEY_AUDIO }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0c, KEY_ZOOM }, /* Full screen */ + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + { 0x10, KEY_PAGEUP }, /* 16-CH PREV */ + { 0x11, KEY_0 }, + { 0x12, KEY_INFO }, + { 0x13, KEY_AGAIN }, /* CH RTN - channel return */ + { 0x14, KEY_MUTE }, + { 0x15, KEY_EDIT }, /* Autoscan */ + { 0x17, KEY_SAVE }, /* Screenshot */ + { 0x18, KEY_PLAYPAUSE }, + { 0x19, KEY_RECORD }, + { 0x1a, KEY_PLAY }, + { 0x1b, KEY_STOP }, + { 0x1c, KEY_FASTFORWARD }, + { 0x1d, KEY_REWIND }, + { 0x1e, KEY_VOLUMEDOWN }, + { 0x1f, KEY_VOLUMEUP }, + { 0x22, KEY_SLEEP }, /* Sleep */ + { 0x23, KEY_ZOOM }, /* Aspect */ + { 0x26, KEY_SCREEN }, /* Pos */ + { 0x27, KEY_ANGLE }, /* Size */ + { 0x28, KEY_SELECT }, /* Select */ + { 0x29, KEY_BLUE }, /* Blue/Picture */ + { 0x2a, KEY_BACKSPACE }, /* Back */ + { 0x2b, KEY_MEDIA }, /* PIP (Picture-in-picture) */ + { 0x2c, KEY_DOWN }, + { 0x2e, KEY_DOT }, + { 0x2f, KEY_TV }, /* Live TV */ + { 0x32, KEY_LEFT }, + { 0x33, KEY_CLEAR }, /* Clear */ + { 0x35, KEY_RED }, /* Red/TV */ + { 0x36, KEY_UP }, + { 0x37, KEY_HOME }, /* Home */ + { 0x39, KEY_GREEN }, /* Green/Video */ + { 0x3d, KEY_YELLOW }, /* Yellow/Music */ + { 0x3e, KEY_OK }, /* Ok */ + { 0x3f, KEY_RIGHT }, + { 0x40, KEY_NEXT }, /* Next */ + { 0x41, KEY_PREVIOUS }, /* Previous */ + { 0x42, KEY_CHANNELDOWN }, /* Channel down */ + { 0x43, KEY_CHANNELUP }, /* Channel up */ +}; + +static struct rc_keymap avermedia_cardbus_map = { + .map = { + .scan = avermedia_cardbus, + .size = ARRAY_SIZE(avermedia_cardbus), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_AVERMEDIA_CARDBUS, + } +}; + +static int __init init_rc_map_avermedia_cardbus(void) +{ + return ir_register_map(&avermedia_cardbus_map); +} + +static void __exit exit_rc_map_avermedia_cardbus(void) +{ + ir_unregister_map(&avermedia_cardbus_map); +} + +module_init(init_rc_map_avermedia_cardbus) +module_exit(exit_rc_map_avermedia_cardbus) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-avermedia-dvbt.c b/drivers/media/IR/keymaps/rc-avermedia-dvbt.c new file mode 100644 index 000000000000..39dde6222875 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-avermedia-dvbt.c @@ -0,0 +1,78 @@ +/* avermedia-dvbt.h - Keytable for avermedia_dvbt Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Matt Jesson <dvb@jesson.eclipse.co.uk */ + +static struct ir_scancode avermedia_dvbt[] = { + { 0x28, KEY_0 }, /* '0' / 'enter' */ + { 0x22, KEY_1 }, /* '1' */ + { 0x12, KEY_2 }, /* '2' / 'up arrow' */ + { 0x32, KEY_3 }, /* '3' */ + { 0x24, KEY_4 }, /* '4' / 'left arrow' */ + { 0x14, KEY_5 }, /* '5' */ + { 0x34, KEY_6 }, /* '6' / 'right arrow' */ + { 0x26, KEY_7 }, /* '7' */ + { 0x16, KEY_8 }, /* '8' / 'down arrow' */ + { 0x36, KEY_9 }, /* '9' */ + + { 0x20, KEY_LIST }, /* 'source' */ + { 0x10, KEY_TEXT }, /* 'teletext' */ + { 0x00, KEY_POWER }, /* 'power' */ + { 0x04, KEY_AUDIO }, /* 'audio' */ + { 0x06, KEY_ZOOM }, /* 'full screen' */ + { 0x18, KEY_VIDEO }, /* 'display' */ + { 0x38, KEY_SEARCH }, /* 'loop' */ + { 0x08, KEY_INFO }, /* 'preview' */ + { 0x2a, KEY_REWIND }, /* 'backward <<' */ + { 0x1a, KEY_FASTFORWARD }, /* 'forward >>' */ + { 0x3a, KEY_RECORD }, /* 'capture' */ + { 0x0a, KEY_MUTE }, /* 'mute' */ + { 0x2c, KEY_RECORD }, /* 'record' */ + { 0x1c, KEY_PAUSE }, /* 'pause' */ + { 0x3c, KEY_STOP }, /* 'stop' */ + { 0x0c, KEY_PLAY }, /* 'play' */ + { 0x2e, KEY_RED }, /* 'red' */ + { 0x01, KEY_BLUE }, /* 'blue' / 'cancel' */ + { 0x0e, KEY_YELLOW }, /* 'yellow' / 'ok' */ + { 0x21, KEY_GREEN }, /* 'green' */ + { 0x11, KEY_CHANNELDOWN }, /* 'channel -' */ + { 0x31, KEY_CHANNELUP }, /* 'channel +' */ + { 0x1e, KEY_VOLUMEDOWN }, /* 'volume -' */ + { 0x3e, KEY_VOLUMEUP }, /* 'volume +' */ +}; + +static struct rc_keymap avermedia_dvbt_map = { + .map = { + .scan = avermedia_dvbt, + .size = ARRAY_SIZE(avermedia_dvbt), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_AVERMEDIA_DVBT, + } +}; + +static int __init init_rc_map_avermedia_dvbt(void) +{ + return ir_register_map(&avermedia_dvbt_map); +} + +static void __exit exit_rc_map_avermedia_dvbt(void) +{ + ir_unregister_map(&avermedia_dvbt_map); +} + +module_init(init_rc_map_avermedia_dvbt) +module_exit(exit_rc_map_avermedia_dvbt) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-avermedia-m135a-rm-jx.c b/drivers/media/IR/keymaps/rc-avermedia-m135a-rm-jx.c new file mode 100644 index 000000000000..101e7ea85941 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-avermedia-m135a-rm-jx.c @@ -0,0 +1,90 @@ +/* avermedia-m135a-rm-jx.h - Keytable for avermedia_m135a_rm_jx Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + * Avermedia M135A with IR model RM-JX + * The same codes exist on both Positivo (BR) and original IR + * Mauro Carvalho Chehab <mchehab@infradead.org> + */ + +static struct ir_scancode avermedia_m135a_rm_jx[] = { + { 0x0200, KEY_POWER2 }, + { 0x022e, KEY_DOT }, /* '.' */ + { 0x0201, KEY_MODE }, /* TV/FM or SOURCE */ + + { 0x0205, KEY_1 }, + { 0x0206, KEY_2 }, + { 0x0207, KEY_3 }, + { 0x0209, KEY_4 }, + { 0x020a, KEY_5 }, + { 0x020b, KEY_6 }, + { 0x020d, KEY_7 }, + { 0x020e, KEY_8 }, + { 0x020f, KEY_9 }, + { 0x0211, KEY_0 }, + + { 0x0213, KEY_RIGHT }, /* -> or L */ + { 0x0212, KEY_LEFT }, /* <- or R */ + + { 0x0217, KEY_SLEEP }, /* Capturar Imagem or Snapshot */ + { 0x0210, KEY_SHUFFLE }, /* Amostra or 16 chan prev */ + + { 0x0303, KEY_CHANNELUP }, + { 0x0302, KEY_CHANNELDOWN }, + { 0x021f, KEY_VOLUMEUP }, + { 0x021e, KEY_VOLUMEDOWN }, + { 0x020c, KEY_ENTER }, /* Full Screen */ + + { 0x0214, KEY_MUTE }, + { 0x0208, KEY_AUDIO }, + + { 0x0203, KEY_TEXT }, /* Teletext */ + { 0x0204, KEY_EPG }, + { 0x022b, KEY_TV2 }, /* TV2 or PIP */ + + { 0x021d, KEY_RED }, + { 0x021c, KEY_YELLOW }, + { 0x0301, KEY_GREEN }, + { 0x0300, KEY_BLUE }, + + { 0x021a, KEY_PLAYPAUSE }, + { 0x0219, KEY_RECORD }, + { 0x0218, KEY_PLAY }, + { 0x021b, KEY_STOP }, +}; + +static struct rc_keymap avermedia_m135a_rm_jx_map = { + .map = { + .scan = avermedia_m135a_rm_jx, + .size = ARRAY_SIZE(avermedia_m135a_rm_jx), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_AVERMEDIA_M135A_RM_JX, + } +}; + +static int __init init_rc_map_avermedia_m135a_rm_jx(void) +{ + return ir_register_map(&avermedia_m135a_rm_jx_map); +} + +static void __exit exit_rc_map_avermedia_m135a_rm_jx(void) +{ + ir_unregister_map(&avermedia_m135a_rm_jx_map); +} + +module_init(init_rc_map_avermedia_m135a_rm_jx) +module_exit(exit_rc_map_avermedia_m135a_rm_jx) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-avermedia.c b/drivers/media/IR/keymaps/rc-avermedia.c new file mode 100644 index 000000000000..21effd5bfb0d --- /dev/null +++ b/drivers/media/IR/keymaps/rc-avermedia.c @@ -0,0 +1,86 @@ +/* avermedia.h - Keytable for avermedia Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Alex Hermann <gaaf@gmx.net> */ + +static struct ir_scancode avermedia[] = { + { 0x28, KEY_1 }, + { 0x18, KEY_2 }, + { 0x38, KEY_3 }, + { 0x24, KEY_4 }, + { 0x14, KEY_5 }, + { 0x34, KEY_6 }, + { 0x2c, KEY_7 }, + { 0x1c, KEY_8 }, + { 0x3c, KEY_9 }, + { 0x22, KEY_0 }, + + { 0x20, KEY_TV }, /* TV/FM */ + { 0x10, KEY_CD }, /* CD */ + { 0x30, KEY_TEXT }, /* TELETEXT */ + { 0x00, KEY_POWER }, /* POWER */ + + { 0x08, KEY_VIDEO }, /* VIDEO */ + { 0x04, KEY_AUDIO }, /* AUDIO */ + { 0x0c, KEY_ZOOM }, /* FULL SCREEN */ + + { 0x12, KEY_SUBTITLE }, /* DISPLAY */ + { 0x32, KEY_REWIND }, /* LOOP */ + { 0x02, KEY_PRINT }, /* PREVIEW */ + + { 0x2a, KEY_SEARCH }, /* AUTOSCAN */ + { 0x1a, KEY_SLEEP }, /* FREEZE */ + { 0x3a, KEY_CAMERA }, /* SNAPSHOT */ + { 0x0a, KEY_MUTE }, /* MUTE */ + + { 0x26, KEY_RECORD }, /* RECORD */ + { 0x16, KEY_PAUSE }, /* PAUSE */ + { 0x36, KEY_STOP }, /* STOP */ + { 0x06, KEY_PLAY }, /* PLAY */ + + { 0x2e, KEY_RED }, /* RED */ + { 0x21, KEY_GREEN }, /* GREEN */ + { 0x0e, KEY_YELLOW }, /* YELLOW */ + { 0x01, KEY_BLUE }, /* BLUE */ + + { 0x1e, KEY_VOLUMEDOWN }, /* VOLUME- */ + { 0x3e, KEY_VOLUMEUP }, /* VOLUME+ */ + { 0x11, KEY_CHANNELDOWN }, /* CHANNEL/PAGE- */ + { 0x31, KEY_CHANNELUP } /* CHANNEL/PAGE+ */ +}; + +static struct rc_keymap avermedia_map = { + .map = { + .scan = avermedia, + .size = ARRAY_SIZE(avermedia), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_AVERMEDIA, + } +}; + +static int __init init_rc_map_avermedia(void) +{ + return ir_register_map(&avermedia_map); +} + +static void __exit exit_rc_map_avermedia(void) +{ + ir_unregister_map(&avermedia_map); +} + +module_init(init_rc_map_avermedia) +module_exit(exit_rc_map_avermedia) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-avertv-303.c b/drivers/media/IR/keymaps/rc-avertv-303.c new file mode 100644 index 000000000000..971c59d6f9d6 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-avertv-303.c @@ -0,0 +1,85 @@ +/* avertv-303.h - Keytable for avertv_303 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* AVERTV STUDIO 303 Remote */ + +static struct ir_scancode avertv_303[] = { + { 0x2a, KEY_1 }, + { 0x32, KEY_2 }, + { 0x3a, KEY_3 }, + { 0x4a, KEY_4 }, + { 0x52, KEY_5 }, + { 0x5a, KEY_6 }, + { 0x6a, KEY_7 }, + { 0x72, KEY_8 }, + { 0x7a, KEY_9 }, + { 0x0e, KEY_0 }, + + { 0x02, KEY_POWER }, + { 0x22, KEY_VIDEO }, + { 0x42, KEY_AUDIO }, + { 0x62, KEY_ZOOM }, + { 0x0a, KEY_TV }, + { 0x12, KEY_CD }, + { 0x1a, KEY_TEXT }, + + { 0x16, KEY_SUBTITLE }, + { 0x1e, KEY_REWIND }, + { 0x06, KEY_PRINT }, + + { 0x2e, KEY_SEARCH }, + { 0x36, KEY_SLEEP }, + { 0x3e, KEY_SHUFFLE }, + { 0x26, KEY_MUTE }, + + { 0x4e, KEY_RECORD }, + { 0x56, KEY_PAUSE }, + { 0x5e, KEY_STOP }, + { 0x46, KEY_PLAY }, + + { 0x6e, KEY_RED }, + { 0x0b, KEY_GREEN }, + { 0x66, KEY_YELLOW }, + { 0x03, KEY_BLUE }, + + { 0x76, KEY_LEFT }, + { 0x7e, KEY_RIGHT }, + { 0x13, KEY_DOWN }, + { 0x1b, KEY_UP }, +}; + +static struct rc_keymap avertv_303_map = { + .map = { + .scan = avertv_303, + .size = ARRAY_SIZE(avertv_303), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_AVERTV_303, + } +}; + +static int __init init_rc_map_avertv_303(void) +{ + return ir_register_map(&avertv_303_map); +} + +static void __exit exit_rc_map_avertv_303(void) +{ + ir_unregister_map(&avertv_303_map); +} + +module_init(init_rc_map_avertv_303) +module_exit(exit_rc_map_avertv_303) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-behold-columbus.c b/drivers/media/IR/keymaps/rc-behold-columbus.c new file mode 100644 index 000000000000..9f56c98fef5b --- /dev/null +++ b/drivers/media/IR/keymaps/rc-behold-columbus.c @@ -0,0 +1,108 @@ +/* behold-columbus.h - Keytable for behold_columbus Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Beholder Intl. Ltd. 2008 + * Dmitry Belimov d.belimov@google.com + * Keytable is used by BeholdTV Columbus + * The "ascii-art picture" below (in comments, first row + * is the keycode in hex, and subsequent row(s) shows + * the button labels (several variants when appropriate) + * helps to descide which keycodes to assign to the buttons. + */ + +static struct ir_scancode behold_columbus[] = { + + /* 0x13 0x11 0x1C 0x12 * + * Mute Source TV/FM Power * + * */ + + { 0x13, KEY_MUTE }, + { 0x11, KEY_PROPS }, + { 0x1C, KEY_TUNER }, /* KEY_TV/KEY_RADIO */ + { 0x12, KEY_POWER }, + + /* 0x01 0x02 0x03 0x0D * + * 1 2 3 Stereo * + * * + * 0x04 0x05 0x06 0x19 * + * 4 5 6 Snapshot * + * * + * 0x07 0x08 0x09 0x10 * + * 7 8 9 Zoom * + * */ + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x0D, KEY_SETUP }, /* Setup key */ + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x19, KEY_CAMERA }, /* Snapshot key */ + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x10, KEY_ZOOM }, + + /* 0x0A 0x00 0x0B 0x0C * + * RECALL 0 ChannelUp VolumeUp * + * */ + { 0x0A, KEY_AGAIN }, + { 0x00, KEY_0 }, + { 0x0B, KEY_CHANNELUP }, + { 0x0C, KEY_VOLUMEUP }, + + /* 0x1B 0x1D 0x15 0x18 * + * Timeshift Record ChannelDown VolumeDown * + * */ + + { 0x1B, KEY_TIME }, + { 0x1D, KEY_RECORD }, + { 0x15, KEY_CHANNELDOWN }, + { 0x18, KEY_VOLUMEDOWN }, + + /* 0x0E 0x1E 0x0F 0x1A * + * Stop Pause Previouse Next * + * */ + + { 0x0E, KEY_STOP }, + { 0x1E, KEY_PAUSE }, + { 0x0F, KEY_PREVIOUS }, + { 0x1A, KEY_NEXT }, + +}; + +static struct rc_keymap behold_columbus_map = { + .map = { + .scan = behold_columbus, + .size = ARRAY_SIZE(behold_columbus), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_BEHOLD_COLUMBUS, + } +}; + +static int __init init_rc_map_behold_columbus(void) +{ + return ir_register_map(&behold_columbus_map); +} + +static void __exit exit_rc_map_behold_columbus(void) +{ + ir_unregister_map(&behold_columbus_map); +} + +module_init(init_rc_map_behold_columbus) +module_exit(exit_rc_map_behold_columbus) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-behold.c b/drivers/media/IR/keymaps/rc-behold.c new file mode 100644 index 000000000000..abc140b2098b --- /dev/null +++ b/drivers/media/IR/keymaps/rc-behold.c @@ -0,0 +1,141 @@ +/* behold.h - Keytable for behold Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + * Igor Kuznetsov <igk72@ya.ru> + * Andrey J. Melnikov <temnota@kmv.ru> + * + * Keytable is used by BeholdTV 60x series, M6 series at + * least, and probably other cards too. + * The "ascii-art picture" below (in comments, first row + * is the keycode in hex, and subsequent row(s) shows + * the button labels (several variants when appropriate) + * helps to descide which keycodes to assign to the buttons. + */ + +static struct ir_scancode behold[] = { + + /* 0x1c 0x12 * + * TV/FM POWER * + * */ + { 0x1c, KEY_TUNER }, /* XXX KEY_TV / KEY_RADIO */ + { 0x12, KEY_POWER }, + + /* 0x01 0x02 0x03 * + * 1 2 3 * + * * + * 0x04 0x05 0x06 * + * 4 5 6 * + * * + * 0x07 0x08 0x09 * + * 7 8 9 * + * */ + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + /* 0x0a 0x00 0x17 * + * RECALL 0 MODE * + * */ + { 0x0a, KEY_AGAIN }, + { 0x00, KEY_0 }, + { 0x17, KEY_MODE }, + + /* 0x14 0x10 * + * ASPECT FULLSCREEN * + * */ + { 0x14, KEY_SCREEN }, + { 0x10, KEY_ZOOM }, + + /* 0x0b * + * Up * + * * + * 0x18 0x16 0x0c * + * Left Ok Right * + * * + * 0x015 * + * Down * + * */ + { 0x0b, KEY_CHANNELUP }, + { 0x18, KEY_VOLUMEDOWN }, + { 0x16, KEY_OK }, /* XXX KEY_ENTER */ + { 0x0c, KEY_VOLUMEUP }, + { 0x15, KEY_CHANNELDOWN }, + + /* 0x11 0x0d * + * MUTE INFO * + * */ + { 0x11, KEY_MUTE }, + { 0x0d, KEY_INFO }, + + /* 0x0f 0x1b 0x1a * + * RECORD PLAY/PAUSE STOP * + * * + * 0x0e 0x1f 0x1e * + *TELETEXT AUDIO SOURCE * + * RED YELLOW * + * */ + { 0x0f, KEY_RECORD }, + { 0x1b, KEY_PLAYPAUSE }, + { 0x1a, KEY_STOP }, + { 0x0e, KEY_TEXT }, + { 0x1f, KEY_RED }, /*XXX KEY_AUDIO */ + { 0x1e, KEY_YELLOW }, /*XXX KEY_SOURCE */ + + /* 0x1d 0x13 0x19 * + * SLEEP PREVIEW DVB * + * GREEN BLUE * + * */ + { 0x1d, KEY_SLEEP }, + { 0x13, KEY_GREEN }, + { 0x19, KEY_BLUE }, /* XXX KEY_SAT */ + + /* 0x58 0x5c * + * FREEZE SNAPSHOT * + * */ + { 0x58, KEY_SLOW }, + { 0x5c, KEY_CAMERA }, + +}; + +static struct rc_keymap behold_map = { + .map = { + .scan = behold, + .size = ARRAY_SIZE(behold), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_BEHOLD, + } +}; + +static int __init init_rc_map_behold(void) +{ + return ir_register_map(&behold_map); +} + +static void __exit exit_rc_map_behold(void) +{ + ir_unregister_map(&behold_map); +} + +module_init(init_rc_map_behold) +module_exit(exit_rc_map_behold) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-budget-ci-old.c b/drivers/media/IR/keymaps/rc-budget-ci-old.c new file mode 100644 index 000000000000..64c2ac913338 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-budget-ci-old.c @@ -0,0 +1,92 @@ +/* budget-ci-old.h - Keytable for budget_ci_old Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* From reading the following remotes: + * Zenith Universal 7 / TV Mode 807 / VCR Mode 837 + * Hauppauge (from NOVA-CI-s box product) + * This is a "middle of the road" approach, differences are noted + */ + +static struct ir_scancode budget_ci_old[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0a, KEY_ENTER }, + { 0x0b, KEY_RED }, + { 0x0c, KEY_POWER }, /* RADIO on Hauppauge */ + { 0x0d, KEY_MUTE }, + { 0x0f, KEY_A }, /* TV on Hauppauge */ + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x14, KEY_B }, + { 0x1c, KEY_UP }, + { 0x1d, KEY_DOWN }, + { 0x1e, KEY_OPTION }, /* RESERVED on Hauppauge */ + { 0x1f, KEY_BREAK }, + { 0x20, KEY_CHANNELUP }, + { 0x21, KEY_CHANNELDOWN }, + { 0x22, KEY_PREVIOUS }, /* Prev Ch on Zenith, SOURCE on Hauppauge */ + { 0x24, KEY_RESTART }, + { 0x25, KEY_OK }, + { 0x26, KEY_CYCLEWINDOWS }, /* MINIMIZE on Hauppauge */ + { 0x28, KEY_ENTER }, /* VCR mode on Zenith */ + { 0x29, KEY_PAUSE }, + { 0x2b, KEY_RIGHT }, + { 0x2c, KEY_LEFT }, + { 0x2e, KEY_MENU }, /* FULL SCREEN on Hauppauge */ + { 0x30, KEY_SLOW }, + { 0x31, KEY_PREVIOUS }, /* VCR mode on Zenith */ + { 0x32, KEY_REWIND }, + { 0x34, KEY_FASTFORWARD }, + { 0x35, KEY_PLAY }, + { 0x36, KEY_STOP }, + { 0x37, KEY_RECORD }, + { 0x38, KEY_TUNER }, /* TV/VCR on Zenith */ + { 0x3a, KEY_C }, + { 0x3c, KEY_EXIT }, + { 0x3d, KEY_POWER2 }, + { 0x3e, KEY_TUNER }, +}; + +static struct rc_keymap budget_ci_old_map = { + .map = { + .scan = budget_ci_old, + .size = ARRAY_SIZE(budget_ci_old), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_BUDGET_CI_OLD, + } +}; + +static int __init init_rc_map_budget_ci_old(void) +{ + return ir_register_map(&budget_ci_old_map); +} + +static void __exit exit_rc_map_budget_ci_old(void) +{ + ir_unregister_map(&budget_ci_old_map); +} + +module_init(init_rc_map_budget_ci_old) +module_exit(exit_rc_map_budget_ci_old) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-cinergy-1400.c b/drivers/media/IR/keymaps/rc-cinergy-1400.c new file mode 100644 index 000000000000..074f2c2c2c61 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-cinergy-1400.c @@ -0,0 +1,84 @@ +/* cinergy-1400.h - Keytable for cinergy_1400 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Cinergy 1400 DVB-T */ + +static struct ir_scancode cinergy_1400[] = { + { 0x01, KEY_POWER }, + { 0x02, KEY_1 }, + { 0x03, KEY_2 }, + { 0x04, KEY_3 }, + { 0x05, KEY_4 }, + { 0x06, KEY_5 }, + { 0x07, KEY_6 }, + { 0x08, KEY_7 }, + { 0x09, KEY_8 }, + { 0x0a, KEY_9 }, + { 0x0c, KEY_0 }, + + { 0x0b, KEY_VIDEO }, + { 0x0d, KEY_REFRESH }, + { 0x0e, KEY_SELECT }, + { 0x0f, KEY_EPG }, + { 0x10, KEY_UP }, + { 0x11, KEY_LEFT }, + { 0x12, KEY_OK }, + { 0x13, KEY_RIGHT }, + { 0x14, KEY_DOWN }, + { 0x15, KEY_TEXT }, + { 0x16, KEY_INFO }, + + { 0x17, KEY_RED }, + { 0x18, KEY_GREEN }, + { 0x19, KEY_YELLOW }, + { 0x1a, KEY_BLUE }, + + { 0x1b, KEY_CHANNELUP }, + { 0x1c, KEY_VOLUMEUP }, + { 0x1d, KEY_MUTE }, + { 0x1e, KEY_VOLUMEDOWN }, + { 0x1f, KEY_CHANNELDOWN }, + + { 0x40, KEY_PAUSE }, + { 0x4c, KEY_PLAY }, + { 0x58, KEY_RECORD }, + { 0x54, KEY_PREVIOUS }, + { 0x48, KEY_STOP }, + { 0x5c, KEY_NEXT }, +}; + +static struct rc_keymap cinergy_1400_map = { + .map = { + .scan = cinergy_1400, + .size = ARRAY_SIZE(cinergy_1400), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_CINERGY_1400, + } +}; + +static int __init init_rc_map_cinergy_1400(void) +{ + return ir_register_map(&cinergy_1400_map); +} + +static void __exit exit_rc_map_cinergy_1400(void) +{ + ir_unregister_map(&cinergy_1400_map); +} + +module_init(init_rc_map_cinergy_1400) +module_exit(exit_rc_map_cinergy_1400) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-cinergy.c b/drivers/media/IR/keymaps/rc-cinergy.c new file mode 100644 index 000000000000..cf84c3dba742 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-cinergy.c @@ -0,0 +1,78 @@ +/* cinergy.h - Keytable for cinergy Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode cinergy[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0a, KEY_POWER }, + { 0x0b, KEY_PROG1 }, /* app */ + { 0x0c, KEY_ZOOM }, /* zoom/fullscreen */ + { 0x0d, KEY_CHANNELUP }, /* channel */ + { 0x0e, KEY_CHANNELDOWN }, /* channel- */ + { 0x0f, KEY_VOLUMEUP }, + { 0x10, KEY_VOLUMEDOWN }, + { 0x11, KEY_TUNER }, /* AV */ + { 0x12, KEY_NUMLOCK }, /* -/-- */ + { 0x13, KEY_AUDIO }, /* audio */ + { 0x14, KEY_MUTE }, + { 0x15, KEY_UP }, + { 0x16, KEY_DOWN }, + { 0x17, KEY_LEFT }, + { 0x18, KEY_RIGHT }, + { 0x19, BTN_LEFT, }, + { 0x1a, BTN_RIGHT, }, + { 0x1b, KEY_WWW }, /* text */ + { 0x1c, KEY_REWIND }, + { 0x1d, KEY_FORWARD }, + { 0x1e, KEY_RECORD }, + { 0x1f, KEY_PLAY }, + { 0x20, KEY_PREVIOUSSONG }, + { 0x21, KEY_NEXTSONG }, + { 0x22, KEY_PAUSE }, + { 0x23, KEY_STOP }, +}; + +static struct rc_keymap cinergy_map = { + .map = { + .scan = cinergy, + .size = ARRAY_SIZE(cinergy), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_CINERGY, + } +}; + +static int __init init_rc_map_cinergy(void) +{ + return ir_register_map(&cinergy_map); +} + +static void __exit exit_rc_map_cinergy(void) +{ + ir_unregister_map(&cinergy_map); +} + +module_init(init_rc_map_cinergy) +module_exit(exit_rc_map_cinergy) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-dm1105-nec.c b/drivers/media/IR/keymaps/rc-dm1105-nec.c new file mode 100644 index 000000000000..90684d0efea3 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-dm1105-nec.c @@ -0,0 +1,76 @@ +/* dm1105-nec.h - Keytable for dm1105_nec Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* DVBWorld remotes + Igor M. Liplianin <liplianin@me.by> + */ + +static struct ir_scancode dm1105_nec[] = { + { 0x0a, KEY_POWER2}, /* power */ + { 0x0c, KEY_MUTE}, /* mute */ + { 0x11, KEY_1}, + { 0x12, KEY_2}, + { 0x13, KEY_3}, + { 0x14, KEY_4}, + { 0x15, KEY_5}, + { 0x16, KEY_6}, + { 0x17, KEY_7}, + { 0x18, KEY_8}, + { 0x19, KEY_9}, + { 0x10, KEY_0}, + { 0x1c, KEY_CHANNELUP}, /* ch+ */ + { 0x0f, KEY_CHANNELDOWN}, /* ch- */ + { 0x1a, KEY_VOLUMEUP}, /* vol+ */ + { 0x0e, KEY_VOLUMEDOWN}, /* vol- */ + { 0x04, KEY_RECORD}, /* rec */ + { 0x09, KEY_CHANNEL}, /* fav */ + { 0x08, KEY_BACKSPACE}, /* rewind */ + { 0x07, KEY_FASTFORWARD}, /* fast */ + { 0x0b, KEY_PAUSE}, /* pause */ + { 0x02, KEY_ESC}, /* cancel */ + { 0x03, KEY_TAB}, /* tab */ + { 0x00, KEY_UP}, /* up */ + { 0x1f, KEY_ENTER}, /* ok */ + { 0x01, KEY_DOWN}, /* down */ + { 0x05, KEY_RECORD}, /* cap */ + { 0x06, KEY_STOP}, /* stop */ + { 0x40, KEY_ZOOM}, /* full */ + { 0x1e, KEY_TV}, /* tvmode */ + { 0x1b, KEY_B}, /* recall */ +}; + +static struct rc_keymap dm1105_nec_map = { + .map = { + .scan = dm1105_nec, + .size = ARRAY_SIZE(dm1105_nec), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_DM1105_NEC, + } +}; + +static int __init init_rc_map_dm1105_nec(void) +{ + return ir_register_map(&dm1105_nec_map); +} + +static void __exit exit_rc_map_dm1105_nec(void) +{ + ir_unregister_map(&dm1105_nec_map); +} + +module_init(init_rc_map_dm1105_nec) +module_exit(exit_rc_map_dm1105_nec) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-dntv-live-dvb-t.c b/drivers/media/IR/keymaps/rc-dntv-live-dvb-t.c new file mode 100644 index 000000000000..8a4027af964a --- /dev/null +++ b/drivers/media/IR/keymaps/rc-dntv-live-dvb-t.c @@ -0,0 +1,78 @@ +/* dntv-live-dvb-t.h - Keytable for dntv_live_dvb_t Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* DigitalNow DNTV Live DVB-T Remote */ + +static struct ir_scancode dntv_live_dvb_t[] = { + { 0x00, KEY_ESC }, /* 'go up a level?' */ + /* Keys 0 to 9 */ + { 0x0a, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0b, KEY_TUNER }, /* tv/fm */ + { 0x0c, KEY_SEARCH }, /* scan */ + { 0x0d, KEY_STOP }, + { 0x0e, KEY_PAUSE }, + { 0x0f, KEY_LIST }, /* source */ + + { 0x10, KEY_MUTE }, + { 0x11, KEY_REWIND }, /* backward << */ + { 0x12, KEY_POWER }, + { 0x13, KEY_CAMERA }, /* snap */ + { 0x14, KEY_AUDIO }, /* stereo */ + { 0x15, KEY_CLEAR }, /* reset */ + { 0x16, KEY_PLAY }, + { 0x17, KEY_ENTER }, + { 0x18, KEY_ZOOM }, /* full screen */ + { 0x19, KEY_FASTFORWARD }, /* forward >> */ + { 0x1a, KEY_CHANNELUP }, + { 0x1b, KEY_VOLUMEUP }, + { 0x1c, KEY_INFO }, /* preview */ + { 0x1d, KEY_RECORD }, /* record */ + { 0x1e, KEY_CHANNELDOWN }, + { 0x1f, KEY_VOLUMEDOWN }, +}; + +static struct rc_keymap dntv_live_dvb_t_map = { + .map = { + .scan = dntv_live_dvb_t, + .size = ARRAY_SIZE(dntv_live_dvb_t), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_DNTV_LIVE_DVB_T, + } +}; + +static int __init init_rc_map_dntv_live_dvb_t(void) +{ + return ir_register_map(&dntv_live_dvb_t_map); +} + +static void __exit exit_rc_map_dntv_live_dvb_t(void) +{ + ir_unregister_map(&dntv_live_dvb_t_map); +} + +module_init(init_rc_map_dntv_live_dvb_t) +module_exit(exit_rc_map_dntv_live_dvb_t) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-dntv-live-dvbt-pro.c b/drivers/media/IR/keymaps/rc-dntv-live-dvbt-pro.c new file mode 100644 index 000000000000..6f4d60764d59 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-dntv-live-dvbt-pro.c @@ -0,0 +1,97 @@ +/* dntv-live-dvbt-pro.h - Keytable for dntv_live_dvbt_pro Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* DigitalNow DNTV Live! DVB-T Pro Remote */ + +static struct ir_scancode dntv_live_dvbt_pro[] = { + { 0x16, KEY_POWER }, + { 0x5b, KEY_HOME }, + + { 0x55, KEY_TV }, /* live tv */ + { 0x58, KEY_TUNER }, /* digital Radio */ + { 0x5a, KEY_RADIO }, /* FM radio */ + { 0x59, KEY_DVD }, /* dvd menu */ + { 0x03, KEY_1 }, + { 0x01, KEY_2 }, + { 0x06, KEY_3 }, + { 0x09, KEY_4 }, + { 0x1d, KEY_5 }, + { 0x1f, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x19, KEY_8 }, + { 0x1b, KEY_9 }, + { 0x0c, KEY_CANCEL }, + { 0x15, KEY_0 }, + { 0x4a, KEY_CLEAR }, + { 0x13, KEY_BACK }, + { 0x00, KEY_TAB }, + { 0x4b, KEY_UP }, + { 0x4e, KEY_LEFT }, + { 0x4f, KEY_OK }, + { 0x52, KEY_RIGHT }, + { 0x51, KEY_DOWN }, + { 0x1e, KEY_VOLUMEUP }, + { 0x0a, KEY_VOLUMEDOWN }, + { 0x02, KEY_CHANNELDOWN }, + { 0x05, KEY_CHANNELUP }, + { 0x11, KEY_RECORD }, + { 0x14, KEY_PLAY }, + { 0x4c, KEY_PAUSE }, + { 0x1a, KEY_STOP }, + { 0x40, KEY_REWIND }, + { 0x12, KEY_FASTFORWARD }, + { 0x41, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x42, KEY_NEXTSONG }, /* skip >| */ + { 0x54, KEY_CAMERA }, /* capture */ + { 0x50, KEY_LANGUAGE }, /* sap */ + { 0x47, KEY_TV2 }, /* pip */ + { 0x4d, KEY_SCREEN }, + { 0x43, KEY_SUBTITLE }, + { 0x10, KEY_MUTE }, + { 0x49, KEY_AUDIO }, /* l/r */ + { 0x07, KEY_SLEEP }, + { 0x08, KEY_VIDEO }, /* a/v */ + { 0x0e, KEY_PREVIOUS }, /* recall */ + { 0x45, KEY_ZOOM }, /* zoom + */ + { 0x46, KEY_ANGLE }, /* zoom - */ + { 0x56, KEY_RED }, + { 0x57, KEY_GREEN }, + { 0x5c, KEY_YELLOW }, + { 0x5d, KEY_BLUE }, +}; + +static struct rc_keymap dntv_live_dvbt_pro_map = { + .map = { + .scan = dntv_live_dvbt_pro, + .size = ARRAY_SIZE(dntv_live_dvbt_pro), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_DNTV_LIVE_DVBT_PRO, + } +}; + +static int __init init_rc_map_dntv_live_dvbt_pro(void) +{ + return ir_register_map(&dntv_live_dvbt_pro_map); +} + +static void __exit exit_rc_map_dntv_live_dvbt_pro(void) +{ + ir_unregister_map(&dntv_live_dvbt_pro_map); +} + +module_init(init_rc_map_dntv_live_dvbt_pro) +module_exit(exit_rc_map_dntv_live_dvbt_pro) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-em-terratec.c b/drivers/media/IR/keymaps/rc-em-terratec.c new file mode 100644 index 000000000000..3130c9c29e6b --- /dev/null +++ b/drivers/media/IR/keymaps/rc-em-terratec.c @@ -0,0 +1,69 @@ +/* em-terratec.h - Keytable for em_terratec Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode em_terratec[] = { + { 0x01, KEY_CHANNEL }, + { 0x02, KEY_SELECT }, + { 0x03, KEY_MUTE }, + { 0x04, KEY_POWER }, + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x08, KEY_CHANNELUP }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0c, KEY_CHANNELDOWN }, + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_0 }, + { 0x12, KEY_MENU }, + { 0x13, KEY_PRINT }, + { 0x14, KEY_VOLUMEDOWN }, + { 0x16, KEY_PAUSE }, + { 0x18, KEY_RECORD }, + { 0x19, KEY_REWIND }, + { 0x1a, KEY_PLAY }, + { 0x1b, KEY_FORWARD }, + { 0x1c, KEY_BACKSPACE }, + { 0x1e, KEY_STOP }, + { 0x40, KEY_ZOOM }, +}; + +static struct rc_keymap em_terratec_map = { + .map = { + .scan = em_terratec, + .size = ARRAY_SIZE(em_terratec), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_EM_TERRATEC, + } +}; + +static int __init init_rc_map_em_terratec(void) +{ + return ir_register_map(&em_terratec_map); +} + +static void __exit exit_rc_map_em_terratec(void) +{ + ir_unregister_map(&em_terratec_map); +} + +module_init(init_rc_map_em_terratec) +module_exit(exit_rc_map_em_terratec) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-empty.c b/drivers/media/IR/keymaps/rc-empty.c new file mode 100644 index 000000000000..3b338d84b476 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-empty.c @@ -0,0 +1,44 @@ +/* empty.h - Keytable for empty Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* empty keytable, can be used as placeholder for not-yet created keytables */ + +static struct ir_scancode empty[] = { + { 0x2a, KEY_COFFEE }, +}; + +static struct rc_keymap empty_map = { + .map = { + .scan = empty, + .size = ARRAY_SIZE(empty), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_EMPTY, + } +}; + +static int __init init_rc_map_empty(void) +{ + return ir_register_map(&empty_map); +} + +static void __exit exit_rc_map_empty(void) +{ + ir_unregister_map(&empty_map); +} + +module_init(init_rc_map_empty) +module_exit(exit_rc_map_empty) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-encore-enltv-fm53.c b/drivers/media/IR/keymaps/rc-encore-enltv-fm53.c new file mode 100644 index 000000000000..4b816967877e --- /dev/null +++ b/drivers/media/IR/keymaps/rc-encore-enltv-fm53.c @@ -0,0 +1,81 @@ +/* encore-enltv-fm53.h - Keytable for encore_enltv_fm53 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Encore ENLTV-FM v5.3 + Mauro Carvalho Chehab <mchehab@infradead.org> + */ + +static struct ir_scancode encore_enltv_fm53[] = { + { 0x10, KEY_POWER2}, + { 0x06, KEY_MUTE}, + + { 0x09, KEY_1}, + { 0x1d, KEY_2}, + { 0x1f, KEY_3}, + { 0x19, KEY_4}, + { 0x1b, KEY_5}, + { 0x11, KEY_6}, + { 0x17, KEY_7}, + { 0x12, KEY_8}, + { 0x16, KEY_9}, + { 0x48, KEY_0}, + + { 0x04, KEY_LIST}, /* -/-- */ + { 0x40, KEY_LAST}, /* recall */ + + { 0x02, KEY_MODE}, /* TV/AV */ + { 0x05, KEY_CAMERA}, /* SNAPSHOT */ + + { 0x4c, KEY_CHANNELUP}, /* UP */ + { 0x00, KEY_CHANNELDOWN}, /* DOWN */ + { 0x0d, KEY_VOLUMEUP}, /* RIGHT */ + { 0x15, KEY_VOLUMEDOWN}, /* LEFT */ + { 0x49, KEY_ENTER}, /* OK */ + + { 0x54, KEY_RECORD}, + { 0x4d, KEY_PLAY}, /* pause */ + + { 0x1e, KEY_MENU}, /* video setting */ + { 0x0e, KEY_RIGHT}, /* <- */ + { 0x1a, KEY_LEFT}, /* -> */ + + { 0x0a, KEY_CLEAR}, /* video default */ + { 0x0c, KEY_ZOOM}, /* hide pannel */ + { 0x47, KEY_SLEEP}, /* shutdown */ +}; + +static struct rc_keymap encore_enltv_fm53_map = { + .map = { + .scan = encore_enltv_fm53, + .size = ARRAY_SIZE(encore_enltv_fm53), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_ENCORE_ENLTV_FM53, + } +}; + +static int __init init_rc_map_encore_enltv_fm53(void) +{ + return ir_register_map(&encore_enltv_fm53_map); +} + +static void __exit exit_rc_map_encore_enltv_fm53(void) +{ + ir_unregister_map(&encore_enltv_fm53_map); +} + +module_init(init_rc_map_encore_enltv_fm53) +module_exit(exit_rc_map_encore_enltv_fm53) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-encore-enltv.c b/drivers/media/IR/keymaps/rc-encore-enltv.c new file mode 100644 index 000000000000..9fabffd28cc9 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-encore-enltv.c @@ -0,0 +1,112 @@ +/* encore-enltv.h - Keytable for encore_enltv Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Encore ENLTV-FM - black plastic, white front cover with white glowing buttons + Juan Pablo Sormani <sorman@gmail.com> */ + +static struct ir_scancode encore_enltv[] = { + + /* Power button does nothing, neither in Windows app, + although it sends data (used for BIOS wakeup?) */ + { 0x0d, KEY_MUTE }, + + { 0x1e, KEY_TV }, + { 0x00, KEY_VIDEO }, + { 0x01, KEY_AUDIO }, /* music */ + { 0x02, KEY_MHP }, /* picture */ + + { 0x1f, KEY_1 }, + { 0x03, KEY_2 }, + { 0x04, KEY_3 }, + { 0x05, KEY_4 }, + { 0x1c, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x1d, KEY_9 }, + { 0x0a, KEY_0 }, + + { 0x09, KEY_LIST }, /* -/-- */ + { 0x0b, KEY_LAST }, /* recall */ + + { 0x14, KEY_HOME }, /* win start menu */ + { 0x15, KEY_EXIT }, /* exit */ + { 0x16, KEY_CHANNELUP }, /* UP */ + { 0x12, KEY_CHANNELDOWN }, /* DOWN */ + { 0x0c, KEY_VOLUMEUP }, /* RIGHT */ + { 0x17, KEY_VOLUMEDOWN }, /* LEFT */ + + { 0x18, KEY_ENTER }, /* OK */ + + { 0x0e, KEY_ESC }, + { 0x13, KEY_CYCLEWINDOWS }, /* desktop */ + { 0x11, KEY_TAB }, + { 0x19, KEY_SWITCHVIDEOMODE }, /* switch */ + + { 0x1a, KEY_MENU }, + { 0x1b, KEY_ZOOM }, /* fullscreen */ + { 0x44, KEY_TIME }, /* time shift */ + { 0x40, KEY_MODE }, /* source */ + + { 0x5a, KEY_RECORD }, + { 0x42, KEY_PLAY }, /* play/pause */ + { 0x45, KEY_STOP }, + { 0x43, KEY_CAMERA }, /* camera icon */ + + { 0x48, KEY_REWIND }, + { 0x4a, KEY_FASTFORWARD }, + { 0x49, KEY_PREVIOUS }, + { 0x4b, KEY_NEXT }, + + { 0x4c, KEY_FAVORITES }, /* tv wall */ + { 0x4d, KEY_SOUND }, /* DVD sound */ + { 0x4e, KEY_LANGUAGE }, /* DVD lang */ + { 0x4f, KEY_TEXT }, /* DVD text */ + + { 0x50, KEY_SLEEP }, /* shutdown */ + { 0x51, KEY_MODE }, /* stereo > main */ + { 0x52, KEY_SELECT }, /* stereo > sap */ + { 0x53, KEY_PROG1 }, /* teletext */ + + + { 0x59, KEY_RED }, /* AP1 */ + { 0x41, KEY_GREEN }, /* AP2 */ + { 0x47, KEY_YELLOW }, /* AP3 */ + { 0x57, KEY_BLUE }, /* AP4 */ +}; + +static struct rc_keymap encore_enltv_map = { + .map = { + .scan = encore_enltv, + .size = ARRAY_SIZE(encore_enltv), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_ENCORE_ENLTV, + } +}; + +static int __init init_rc_map_encore_enltv(void) +{ + return ir_register_map(&encore_enltv_map); +} + +static void __exit exit_rc_map_encore_enltv(void) +{ + ir_unregister_map(&encore_enltv_map); +} + +module_init(init_rc_map_encore_enltv) +module_exit(exit_rc_map_encore_enltv) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-encore-enltv2.c b/drivers/media/IR/keymaps/rc-encore-enltv2.c new file mode 100644 index 000000000000..efefd5166618 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-encore-enltv2.c @@ -0,0 +1,90 @@ +/* encore-enltv2.h - Keytable for encore_enltv2 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Encore ENLTV2-FM - silver plastic - "Wand Media" written at the botton + Mauro Carvalho Chehab <mchehab@infradead.org> */ + +static struct ir_scancode encore_enltv2[] = { + { 0x4c, KEY_POWER2 }, + { 0x4a, KEY_TUNER }, + { 0x40, KEY_1 }, + { 0x60, KEY_2 }, + { 0x50, KEY_3 }, + { 0x70, KEY_4 }, + { 0x48, KEY_5 }, + { 0x68, KEY_6 }, + { 0x58, KEY_7 }, + { 0x78, KEY_8 }, + { 0x44, KEY_9 }, + { 0x54, KEY_0 }, + + { 0x64, KEY_LAST }, /* +100 */ + { 0x4e, KEY_AGAIN }, /* Recall */ + + { 0x6c, KEY_SWITCHVIDEOMODE }, /* Video Source */ + { 0x5e, KEY_MENU }, + { 0x56, KEY_SCREEN }, + { 0x7a, KEY_SETUP }, + + { 0x46, KEY_MUTE }, + { 0x5c, KEY_MODE }, /* Stereo */ + { 0x74, KEY_INFO }, + { 0x7c, KEY_CLEAR }, + + { 0x55, KEY_UP }, + { 0x49, KEY_DOWN }, + { 0x7e, KEY_LEFT }, + { 0x59, KEY_RIGHT }, + { 0x6a, KEY_ENTER }, + + { 0x42, KEY_VOLUMEUP }, + { 0x62, KEY_VOLUMEDOWN }, + { 0x52, KEY_CHANNELUP }, + { 0x72, KEY_CHANNELDOWN }, + + { 0x41, KEY_RECORD }, + { 0x51, KEY_CAMERA }, /* Snapshot */ + { 0x75, KEY_TIME }, /* Timeshift */ + { 0x71, KEY_TV2 }, /* PIP */ + + { 0x45, KEY_REWIND }, + { 0x6f, KEY_PAUSE }, + { 0x7d, KEY_FORWARD }, + { 0x79, KEY_STOP }, +}; + +static struct rc_keymap encore_enltv2_map = { + .map = { + .scan = encore_enltv2, + .size = ARRAY_SIZE(encore_enltv2), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_ENCORE_ENLTV2, + } +}; + +static int __init init_rc_map_encore_enltv2(void) +{ + return ir_register_map(&encore_enltv2_map); +} + +static void __exit exit_rc_map_encore_enltv2(void) +{ + ir_unregister_map(&encore_enltv2_map); +} + +module_init(init_rc_map_encore_enltv2) +module_exit(exit_rc_map_encore_enltv2) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-evga-indtube.c b/drivers/media/IR/keymaps/rc-evga-indtube.c new file mode 100644 index 000000000000..3f3fb13813b3 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-evga-indtube.c @@ -0,0 +1,61 @@ +/* evga-indtube.h - Keytable for evga_indtube Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* EVGA inDtube + Devin Heitmueller <devin.heitmueller@gmail.com> + */ + +static struct ir_scancode evga_indtube[] = { + { 0x12, KEY_POWER}, + { 0x02, KEY_MODE}, /* TV */ + { 0x14, KEY_MUTE}, + { 0x1a, KEY_CHANNELUP}, + { 0x16, KEY_TV2}, /* PIP */ + { 0x1d, KEY_VOLUMEUP}, + { 0x05, KEY_CHANNELDOWN}, + { 0x0f, KEY_PLAYPAUSE}, + { 0x19, KEY_VOLUMEDOWN}, + { 0x1c, KEY_REWIND}, + { 0x0d, KEY_RECORD}, + { 0x18, KEY_FORWARD}, + { 0x1e, KEY_PREVIOUS}, + { 0x1b, KEY_STOP}, + { 0x1f, KEY_NEXT}, + { 0x13, KEY_CAMERA}, +}; + +static struct rc_keymap evga_indtube_map = { + .map = { + .scan = evga_indtube, + .size = ARRAY_SIZE(evga_indtube), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_EVGA_INDTUBE, + } +}; + +static int __init init_rc_map_evga_indtube(void) +{ + return ir_register_map(&evga_indtube_map); +} + +static void __exit exit_rc_map_evga_indtube(void) +{ + ir_unregister_map(&evga_indtube_map); +} + +module_init(init_rc_map_evga_indtube) +module_exit(exit_rc_map_evga_indtube) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-eztv.c b/drivers/media/IR/keymaps/rc-eztv.c new file mode 100644 index 000000000000..660907a78db9 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-eztv.c @@ -0,0 +1,96 @@ +/* eztv.h - Keytable for eztv Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Alfons Geser <a.geser@cox.net> + * updates from Job D. R. Borges <jobdrb@ig.com.br> */ + +static struct ir_scancode eztv[] = { + { 0x12, KEY_POWER }, + { 0x01, KEY_TV }, /* DVR */ + { 0x15, KEY_DVD }, /* DVD */ + { 0x17, KEY_AUDIO }, /* music */ + /* DVR mode / DVD mode / music mode */ + + { 0x1b, KEY_MUTE }, /* mute */ + { 0x02, KEY_LANGUAGE }, /* MTS/SAP / audio / autoseek */ + { 0x1e, KEY_SUBTITLE }, /* closed captioning / subtitle / seek */ + { 0x16, KEY_ZOOM }, /* full screen */ + { 0x1c, KEY_VIDEO }, /* video source / eject / delall */ + { 0x1d, KEY_RESTART }, /* playback / angle / del */ + { 0x2f, KEY_SEARCH }, /* scan / menu / playlist */ + { 0x30, KEY_CHANNEL }, /* CH surfing / bookmark / memo */ + + { 0x31, KEY_HELP }, /* help */ + { 0x32, KEY_MODE }, /* num/memo */ + { 0x33, KEY_ESC }, /* cancel */ + + { 0x0c, KEY_UP }, /* up */ + { 0x10, KEY_DOWN }, /* down */ + { 0x08, KEY_LEFT }, /* left */ + { 0x04, KEY_RIGHT }, /* right */ + { 0x03, KEY_SELECT }, /* select */ + + { 0x1f, KEY_REWIND }, /* rewind */ + { 0x20, KEY_PLAYPAUSE },/* play/pause */ + { 0x29, KEY_FORWARD }, /* forward */ + { 0x14, KEY_AGAIN }, /* repeat */ + { 0x2b, KEY_RECORD }, /* recording */ + { 0x2c, KEY_STOP }, /* stop */ + { 0x2d, KEY_PLAY }, /* play */ + { 0x2e, KEY_CAMERA }, /* snapshot / shuffle */ + + { 0x00, KEY_0 }, + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + + { 0x2a, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x18, KEY_CHANNELUP },/* CH.tracking up */ + { 0x19, KEY_CHANNELDOWN },/* CH.tracking down */ + + { 0x13, KEY_ENTER }, /* enter */ + { 0x21, KEY_DOT }, /* . (decimal dot) */ +}; + +static struct rc_keymap eztv_map = { + .map = { + .scan = eztv, + .size = ARRAY_SIZE(eztv), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_EZTV, + } +}; + +static int __init init_rc_map_eztv(void) +{ + return ir_register_map(&eztv_map); +} + +static void __exit exit_rc_map_eztv(void) +{ + ir_unregister_map(&eztv_map); +} + +module_init(init_rc_map_eztv) +module_exit(exit_rc_map_eztv) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-flydvb.c b/drivers/media/IR/keymaps/rc-flydvb.c new file mode 100644 index 000000000000..a173c81035f4 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-flydvb.c @@ -0,0 +1,77 @@ +/* flydvb.h - Keytable for flydvb Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode flydvb[] = { + { 0x01, KEY_ZOOM }, /* Full Screen */ + { 0x00, KEY_POWER }, /* Power */ + + { 0x03, KEY_1 }, + { 0x04, KEY_2 }, + { 0x05, KEY_3 }, + { 0x07, KEY_4 }, + { 0x08, KEY_5 }, + { 0x09, KEY_6 }, + { 0x0b, KEY_7 }, + { 0x0c, KEY_8 }, + { 0x0d, KEY_9 }, + { 0x06, KEY_AGAIN }, /* Recall */ + { 0x0f, KEY_0 }, + { 0x10, KEY_MUTE }, /* Mute */ + { 0x02, KEY_RADIO }, /* TV/Radio */ + { 0x1b, KEY_LANGUAGE }, /* SAP (Second Audio Program) */ + + { 0x14, KEY_VOLUMEUP }, /* VOL+ */ + { 0x17, KEY_VOLUMEDOWN }, /* VOL- */ + { 0x12, KEY_CHANNELUP }, /* CH+ */ + { 0x13, KEY_CHANNELDOWN }, /* CH- */ + { 0x1d, KEY_ENTER }, /* Enter */ + + { 0x1a, KEY_MODE }, /* PIP */ + { 0x18, KEY_TUNER }, /* Source */ + + { 0x1e, KEY_RECORD }, /* Record/Pause */ + { 0x15, KEY_ANGLE }, /* Swap (no label on key) */ + { 0x1c, KEY_PAUSE }, /* Timeshift/Pause */ + { 0x19, KEY_BACK }, /* Rewind << */ + { 0x0a, KEY_PLAYPAUSE }, /* Play/Pause */ + { 0x1f, KEY_FORWARD }, /* Forward >> */ + { 0x16, KEY_PREVIOUS }, /* Back |<< */ + { 0x11, KEY_STOP }, /* Stop */ + { 0x0e, KEY_NEXT }, /* End >>| */ +}; + +static struct rc_keymap flydvb_map = { + .map = { + .scan = flydvb, + .size = ARRAY_SIZE(flydvb), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_FLYDVB, + } +}; + +static int __init init_rc_map_flydvb(void) +{ + return ir_register_map(&flydvb_map); +} + +static void __exit exit_rc_map_flydvb(void) +{ + ir_unregister_map(&flydvb_map); +} + +module_init(init_rc_map_flydvb) +module_exit(exit_rc_map_flydvb) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-flyvideo.c b/drivers/media/IR/keymaps/rc-flyvideo.c new file mode 100644 index 000000000000..9c73043cbdba --- /dev/null +++ b/drivers/media/IR/keymaps/rc-flyvideo.c @@ -0,0 +1,70 @@ +/* flyvideo.h - Keytable for flyvideo Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode flyvideo[] = { + { 0x0f, KEY_0 }, + { 0x03, KEY_1 }, + { 0x04, KEY_2 }, + { 0x05, KEY_3 }, + { 0x07, KEY_4 }, + { 0x08, KEY_5 }, + { 0x09, KEY_6 }, + { 0x0b, KEY_7 }, + { 0x0c, KEY_8 }, + { 0x0d, KEY_9 }, + + { 0x0e, KEY_MODE }, /* Air/Cable */ + { 0x11, KEY_VIDEO }, /* Video */ + { 0x15, KEY_AUDIO }, /* Audio */ + { 0x00, KEY_POWER }, /* Power */ + { 0x18, KEY_TUNER }, /* AV Source */ + { 0x02, KEY_ZOOM }, /* Fullscreen */ + { 0x1a, KEY_LANGUAGE }, /* Stereo */ + { 0x1b, KEY_MUTE }, /* Mute */ + { 0x14, KEY_VOLUMEUP }, /* Volume + */ + { 0x17, KEY_VOLUMEDOWN },/* Volume - */ + { 0x12, KEY_CHANNELUP },/* Channel + */ + { 0x13, KEY_CHANNELDOWN },/* Channel - */ + { 0x06, KEY_AGAIN }, /* Recall */ + { 0x10, KEY_ENTER }, /* Enter */ + + { 0x19, KEY_BACK }, /* Rewind ( <<< ) */ + { 0x1f, KEY_FORWARD }, /* Forward ( >>> ) */ + { 0x0a, KEY_ANGLE }, /* no label, may be used as the PAUSE button */ +}; + +static struct rc_keymap flyvideo_map = { + .map = { + .scan = flyvideo, + .size = ARRAY_SIZE(flyvideo), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_FLYVIDEO, + } +}; + +static int __init init_rc_map_flyvideo(void) +{ + return ir_register_map(&flyvideo_map); +} + +static void __exit exit_rc_map_flyvideo(void) +{ + ir_unregister_map(&flyvideo_map); +} + +module_init(init_rc_map_flyvideo) +module_exit(exit_rc_map_flyvideo) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-fusionhdtv-mce.c b/drivers/media/IR/keymaps/rc-fusionhdtv-mce.c new file mode 100644 index 000000000000..cdb10389b10e --- /dev/null +++ b/drivers/media/IR/keymaps/rc-fusionhdtv-mce.c @@ -0,0 +1,98 @@ +/* fusionhdtv-mce.h - Keytable for fusionhdtv_mce Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* DViCO FUSION HDTV MCE remote */ + +static struct ir_scancode fusionhdtv_mce[] = { + + { 0x0b, KEY_1 }, + { 0x17, KEY_2 }, + { 0x1b, KEY_3 }, + { 0x07, KEY_4 }, + { 0x50, KEY_5 }, + { 0x54, KEY_6 }, + { 0x48, KEY_7 }, + { 0x4c, KEY_8 }, + { 0x58, KEY_9 }, + { 0x03, KEY_0 }, + + { 0x5e, KEY_OK }, + { 0x51, KEY_UP }, + { 0x53, KEY_DOWN }, + { 0x5b, KEY_LEFT }, + { 0x5f, KEY_RIGHT }, + + { 0x02, KEY_TV }, /* Labeled DTV on remote */ + { 0x0e, KEY_MP3 }, + { 0x1a, KEY_DVD }, + { 0x1e, KEY_FAVORITES }, /* Labeled CPF on remote */ + { 0x16, KEY_SETUP }, + { 0x46, KEY_POWER2 }, /* TV On/Off button on remote */ + { 0x0a, KEY_EPG }, /* Labeled Guide on remote */ + + { 0x49, KEY_BACK }, + { 0x59, KEY_INFO }, /* Labeled MORE on remote */ + { 0x4d, KEY_MENU }, /* Labeled DVDMENU on remote */ + { 0x55, KEY_CYCLEWINDOWS }, /* Labeled ALT-TAB on remote */ + + { 0x0f, KEY_PREVIOUSSONG }, /* Labeled |<< REPLAY on remote */ + { 0x12, KEY_NEXTSONG }, /* Labeled >>| SKIP on remote */ + { 0x42, KEY_ENTER }, /* Labeled START with a green + MS windows logo on remote */ + + { 0x15, KEY_VOLUMEUP }, + { 0x05, KEY_VOLUMEDOWN }, + { 0x11, KEY_CHANNELUP }, + { 0x09, KEY_CHANNELDOWN }, + + { 0x52, KEY_CAMERA }, + { 0x5a, KEY_TUNER }, + { 0x19, KEY_OPEN }, + + { 0x13, KEY_MODE }, /* 4:3 16:9 select */ + { 0x1f, KEY_ZOOM }, + + { 0x43, KEY_REWIND }, + { 0x47, KEY_PLAYPAUSE }, + { 0x4f, KEY_FASTFORWARD }, + { 0x57, KEY_MUTE }, + { 0x0d, KEY_STOP }, + { 0x01, KEY_RECORD }, + { 0x4e, KEY_POWER }, +}; + +static struct rc_keymap fusionhdtv_mce_map = { + .map = { + .scan = fusionhdtv_mce, + .size = ARRAY_SIZE(fusionhdtv_mce), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_FUSIONHDTV_MCE, + } +}; + +static int __init init_rc_map_fusionhdtv_mce(void) +{ + return ir_register_map(&fusionhdtv_mce_map); +} + +static void __exit exit_rc_map_fusionhdtv_mce(void) +{ + ir_unregister_map(&fusionhdtv_mce_map); +} + +module_init(init_rc_map_fusionhdtv_mce) +module_exit(exit_rc_map_fusionhdtv_mce) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-gadmei-rm008z.c b/drivers/media/IR/keymaps/rc-gadmei-rm008z.c new file mode 100644 index 000000000000..c16c0d1263ac --- /dev/null +++ b/drivers/media/IR/keymaps/rc-gadmei-rm008z.c @@ -0,0 +1,81 @@ +/* gadmei-rm008z.h - Keytable for gadmei_rm008z Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* GADMEI UTV330+ RM008Z remote + Shine Liu <shinel@foxmail.com> + */ + +static struct ir_scancode gadmei_rm008z[] = { + { 0x14, KEY_POWER2}, /* POWER OFF */ + { 0x0c, KEY_MUTE}, /* MUTE */ + + { 0x18, KEY_TV}, /* TV */ + { 0x0e, KEY_VIDEO}, /* AV */ + { 0x0b, KEY_AUDIO}, /* SV */ + { 0x0f, KEY_RADIO}, /* FM */ + + { 0x00, KEY_1}, + { 0x01, KEY_2}, + { 0x02, KEY_3}, + { 0x03, KEY_4}, + { 0x04, KEY_5}, + { 0x05, KEY_6}, + { 0x06, KEY_7}, + { 0x07, KEY_8}, + { 0x08, KEY_9}, + { 0x09, KEY_0}, + { 0x0a, KEY_INFO}, /* OSD */ + { 0x1c, KEY_BACKSPACE}, /* LAST */ + + { 0x0d, KEY_PLAY}, /* PLAY */ + { 0x1e, KEY_CAMERA}, /* SNAPSHOT */ + { 0x1a, KEY_RECORD}, /* RECORD */ + { 0x17, KEY_STOP}, /* STOP */ + + { 0x1f, KEY_UP}, /* UP */ + { 0x44, KEY_DOWN}, /* DOWN */ + { 0x46, KEY_TAB}, /* BACK */ + { 0x4a, KEY_ZOOM}, /* FULLSECREEN */ + + { 0x10, KEY_VOLUMEUP}, /* VOLUMEUP */ + { 0x11, KEY_VOLUMEDOWN}, /* VOLUMEDOWN */ + { 0x12, KEY_CHANNELUP}, /* CHANNELUP */ + { 0x13, KEY_CHANNELDOWN}, /* CHANNELDOWN */ + { 0x15, KEY_ENTER}, /* OK */ +}; + +static struct rc_keymap gadmei_rm008z_map = { + .map = { + .scan = gadmei_rm008z, + .size = ARRAY_SIZE(gadmei_rm008z), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_GADMEI_RM008Z, + } +}; + +static int __init init_rc_map_gadmei_rm008z(void) +{ + return ir_register_map(&gadmei_rm008z_map); +} + +static void __exit exit_rc_map_gadmei_rm008z(void) +{ + ir_unregister_map(&gadmei_rm008z_map); +} + +module_init(init_rc_map_gadmei_rm008z) +module_exit(exit_rc_map_gadmei_rm008z) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-genius-tvgo-a11mce.c b/drivers/media/IR/keymaps/rc-genius-tvgo-a11mce.c new file mode 100644 index 000000000000..89f8e384e52a --- /dev/null +++ b/drivers/media/IR/keymaps/rc-genius-tvgo-a11mce.c @@ -0,0 +1,84 @@ +/* genius-tvgo-a11mce.h - Keytable for genius_tvgo_a11mce Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + * Remote control for the Genius TVGO A11MCE + * Adrian Pardini <pardo.bsso@gmail.com> + */ + +static struct ir_scancode genius_tvgo_a11mce[] = { + /* Keys 0 to 9 */ + { 0x48, KEY_0 }, + { 0x09, KEY_1 }, + { 0x1d, KEY_2 }, + { 0x1f, KEY_3 }, + { 0x19, KEY_4 }, + { 0x1b, KEY_5 }, + { 0x11, KEY_6 }, + { 0x17, KEY_7 }, + { 0x12, KEY_8 }, + { 0x16, KEY_9 }, + + { 0x54, KEY_RECORD }, /* recording */ + { 0x06, KEY_MUTE }, /* mute */ + { 0x10, KEY_POWER }, + { 0x40, KEY_LAST }, /* recall */ + { 0x4c, KEY_CHANNELUP }, /* channel / program + */ + { 0x00, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x0d, KEY_VOLUMEUP }, + { 0x15, KEY_VOLUMEDOWN }, + { 0x4d, KEY_OK }, /* also labeled as Pause */ + { 0x1c, KEY_ZOOM }, /* full screen and Stop*/ + { 0x02, KEY_MODE }, /* AV Source or Rewind*/ + { 0x04, KEY_LIST }, /* -/-- */ + /* small arrows above numbers */ + { 0x1a, KEY_NEXT }, /* also Fast Forward */ + { 0x0e, KEY_PREVIOUS }, /* also Rewind */ + /* these are in a rather non standard layout and have + an alternate name written */ + { 0x1e, KEY_UP }, /* Video Setting */ + { 0x0a, KEY_DOWN }, /* Video Default */ + { 0x05, KEY_CAMERA }, /* Snapshot */ + { 0x0c, KEY_RIGHT }, /* Hide Panel */ + /* Four buttons without label */ + { 0x49, KEY_RED }, + { 0x0b, KEY_GREEN }, + { 0x13, KEY_YELLOW }, + { 0x50, KEY_BLUE }, +}; + +static struct rc_keymap genius_tvgo_a11mce_map = { + .map = { + .scan = genius_tvgo_a11mce, + .size = ARRAY_SIZE(genius_tvgo_a11mce), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_GENIUS_TVGO_A11MCE, + } +}; + +static int __init init_rc_map_genius_tvgo_a11mce(void) +{ + return ir_register_map(&genius_tvgo_a11mce_map); +} + +static void __exit exit_rc_map_genius_tvgo_a11mce(void) +{ + ir_unregister_map(&genius_tvgo_a11mce_map); +} + +module_init(init_rc_map_genius_tvgo_a11mce) +module_exit(exit_rc_map_genius_tvgo_a11mce) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-gotview7135.c b/drivers/media/IR/keymaps/rc-gotview7135.c new file mode 100644 index 000000000000..52f025bb35f6 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-gotview7135.c @@ -0,0 +1,79 @@ +/* gotview7135.h - Keytable for gotview7135 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Mike Baikov <mike@baikov.com> */ + +static struct ir_scancode gotview7135[] = { + + { 0x11, KEY_POWER }, + { 0x35, KEY_TV }, + { 0x1b, KEY_0 }, + { 0x29, KEY_1 }, + { 0x19, KEY_2 }, + { 0x39, KEY_3 }, + { 0x1f, KEY_4 }, + { 0x2c, KEY_5 }, + { 0x21, KEY_6 }, + { 0x24, KEY_7 }, + { 0x18, KEY_8 }, + { 0x2b, KEY_9 }, + { 0x3b, KEY_AGAIN }, /* LOOP */ + { 0x06, KEY_AUDIO }, + { 0x31, KEY_PRINT }, /* PREVIEW */ + { 0x3e, KEY_VIDEO }, + { 0x10, KEY_CHANNELUP }, + { 0x20, KEY_CHANNELDOWN }, + { 0x0c, KEY_VOLUMEDOWN }, + { 0x28, KEY_VOLUMEUP }, + { 0x08, KEY_MUTE }, + { 0x26, KEY_SEARCH }, /* SCAN */ + { 0x3f, KEY_CAMERA }, /* SNAPSHOT */ + { 0x12, KEY_RECORD }, + { 0x32, KEY_STOP }, + { 0x3c, KEY_PLAY }, + { 0x1d, KEY_REWIND }, + { 0x2d, KEY_PAUSE }, + { 0x0d, KEY_FORWARD }, + { 0x05, KEY_ZOOM }, /*FULL*/ + + { 0x2a, KEY_F21 }, /* LIVE TIMESHIFT */ + { 0x0e, KEY_F22 }, /* MIN TIMESHIFT */ + { 0x1e, KEY_TIME }, /* TIMESHIFT */ + { 0x38, KEY_F24 }, /* NORMAL TIMESHIFT */ +}; + +static struct rc_keymap gotview7135_map = { + .map = { + .scan = gotview7135, + .size = ARRAY_SIZE(gotview7135), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_GOTVIEW7135, + } +}; + +static int __init init_rc_map_gotview7135(void) +{ + return ir_register_map(&gotview7135_map); +} + +static void __exit exit_rc_map_gotview7135(void) +{ + ir_unregister_map(&gotview7135_map); +} + +module_init(init_rc_map_gotview7135) +module_exit(exit_rc_map_gotview7135) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-hauppauge-new.c b/drivers/media/IR/keymaps/rc-hauppauge-new.c new file mode 100644 index 000000000000..c6f8cd7c5186 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-hauppauge-new.c @@ -0,0 +1,100 @@ +/* hauppauge-new.h - Keytable for hauppauge_new Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Hauppauge: the newer, gray remotes (seems there are multiple + * slightly different versions), shipped with cx88+ivtv cards. + * almost rc5 coding, but some non-standard keys */ + +static struct ir_scancode hauppauge_new[] = { + /* Keys 0 to 9 */ + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0a, KEY_TEXT }, /* keypad asterisk as well */ + { 0x0b, KEY_RED }, /* red button */ + { 0x0c, KEY_RADIO }, + { 0x0d, KEY_MENU }, + { 0x0e, KEY_SUBTITLE }, /* also the # key */ + { 0x0f, KEY_MUTE }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x12, KEY_PREVIOUS }, /* previous channel */ + { 0x14, KEY_UP }, + { 0x15, KEY_DOWN }, + { 0x16, KEY_LEFT }, + { 0x17, KEY_RIGHT }, + { 0x18, KEY_VIDEO }, /* Videos */ + { 0x19, KEY_AUDIO }, /* Music */ + /* 0x1a: Pictures - presume this means + "Multimedia Home Platform" - + no "PICTURES" key in input.h + */ + { 0x1a, KEY_MHP }, + + { 0x1b, KEY_EPG }, /* Guide */ + { 0x1c, KEY_TV }, + { 0x1e, KEY_NEXTSONG }, /* skip >| */ + { 0x1f, KEY_EXIT }, /* back/exit */ + { 0x20, KEY_CHANNELUP }, /* channel / program + */ + { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x22, KEY_CHANNEL }, /* source (old black remote) */ + { 0x24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x25, KEY_ENTER }, /* OK */ + { 0x26, KEY_SLEEP }, /* minimize (old black remote) */ + { 0x29, KEY_BLUE }, /* blue key */ + { 0x2e, KEY_GREEN }, /* green button */ + { 0x30, KEY_PAUSE }, /* pause */ + { 0x32, KEY_REWIND }, /* backward << */ + { 0x34, KEY_FASTFORWARD }, /* forward >> */ + { 0x35, KEY_PLAY }, + { 0x36, KEY_STOP }, + { 0x37, KEY_RECORD }, /* recording */ + { 0x38, KEY_YELLOW }, /* yellow key */ + { 0x3b, KEY_SELECT }, /* top right button */ + { 0x3c, KEY_ZOOM }, /* full */ + { 0x3d, KEY_POWER }, /* system power (green button) */ +}; + +static struct rc_keymap hauppauge_new_map = { + .map = { + .scan = hauppauge_new, + .size = ARRAY_SIZE(hauppauge_new), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_HAUPPAUGE_NEW, + } +}; + +static int __init init_rc_map_hauppauge_new(void) +{ + return ir_register_map(&hauppauge_new_map); +} + +static void __exit exit_rc_map_hauppauge_new(void) +{ + ir_unregister_map(&hauppauge_new_map); +} + +module_init(init_rc_map_hauppauge_new) +module_exit(exit_rc_map_hauppauge_new) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-imon-mce.c b/drivers/media/IR/keymaps/rc-imon-mce.c new file mode 100644 index 000000000000..e49f350e3a0d --- /dev/null +++ b/drivers/media/IR/keymaps/rc-imon-mce.c @@ -0,0 +1,142 @@ +/* rc5-imon-mce.c - Keytable for Windows Media Center RC-6 remotes for use + * with the SoundGraph iMON/Antec Veris hardware IR decoder + * + * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* mce-mode imon mce remote key table */ +static struct ir_scancode imon_mce[] = { + /* keys sorted mostly by frequency of use to optimize lookups */ + { 0x800ff415, KEY_REWIND }, + { 0x800ff414, KEY_FASTFORWARD }, + { 0x800ff41b, KEY_PREVIOUS }, + { 0x800ff41a, KEY_NEXT }, + + { 0x800ff416, KEY_PLAY }, + { 0x800ff418, KEY_PAUSE }, + { 0x800ff419, KEY_STOP }, + { 0x800ff417, KEY_RECORD }, + + { 0x02000052, KEY_UP }, + { 0x02000051, KEY_DOWN }, + { 0x02000050, KEY_LEFT }, + { 0x0200004f, KEY_RIGHT }, + + { 0x800ff41e, KEY_UP }, + { 0x800ff41f, KEY_DOWN }, + { 0x800ff420, KEY_LEFT }, + { 0x800ff421, KEY_RIGHT }, + + /* 0x800ff40b also KEY_NUMERIC_POUND on some receivers */ + { 0x800ff40b, KEY_ENTER }, + { 0x02000028, KEY_ENTER }, +/* the OK and Enter buttons decode to the same value on some remotes + { 0x02000028, KEY_OK }, */ + { 0x800ff422, KEY_OK }, + { 0x0200002a, KEY_EXIT }, + { 0x800ff423, KEY_EXIT }, + { 0x02000029, KEY_DELETE }, + /* 0x800ff40a also KEY_NUMERIC_STAR on some receivers */ + { 0x800ff40a, KEY_DELETE }, + + { 0x800ff40e, KEY_MUTE }, + { 0x800ff410, KEY_VOLUMEUP }, + { 0x800ff411, KEY_VOLUMEDOWN }, + { 0x800ff412, KEY_CHANNELUP }, + { 0x800ff413, KEY_CHANNELDOWN }, + + { 0x0200001e, KEY_NUMERIC_1 }, + { 0x0200001f, KEY_NUMERIC_2 }, + { 0x02000020, KEY_NUMERIC_3 }, + { 0x02000021, KEY_NUMERIC_4 }, + { 0x02000022, KEY_NUMERIC_5 }, + { 0x02000023, KEY_NUMERIC_6 }, + { 0x02000024, KEY_NUMERIC_7 }, + { 0x02000025, KEY_NUMERIC_8 }, + { 0x02000026, KEY_NUMERIC_9 }, + { 0x02000027, KEY_NUMERIC_0 }, + + { 0x800ff401, KEY_NUMERIC_1 }, + { 0x800ff402, KEY_NUMERIC_2 }, + { 0x800ff403, KEY_NUMERIC_3 }, + { 0x800ff404, KEY_NUMERIC_4 }, + { 0x800ff405, KEY_NUMERIC_5 }, + { 0x800ff406, KEY_NUMERIC_6 }, + { 0x800ff407, KEY_NUMERIC_7 }, + { 0x800ff408, KEY_NUMERIC_8 }, + { 0x800ff409, KEY_NUMERIC_9 }, + { 0x800ff400, KEY_NUMERIC_0 }, + + { 0x02200025, KEY_NUMERIC_STAR }, + { 0x02200020, KEY_NUMERIC_POUND }, + /* 0x800ff41d also KEY_BLUE on some receivers */ + { 0x800ff41d, KEY_NUMERIC_STAR }, + /* 0x800ff41c also KEY_PREVIOUS on some receivers */ + { 0x800ff41c, KEY_NUMERIC_POUND }, + + { 0x800ff446, KEY_TV }, + { 0x800ff447, KEY_AUDIO }, /* My Music */ + { 0x800ff448, KEY_PVR }, /* RecordedTV */ + { 0x800ff449, KEY_CAMERA }, + { 0x800ff44a, KEY_VIDEO }, + /* 0x800ff424 also KEY_MENU on some receivers */ + { 0x800ff424, KEY_DVD }, + /* 0x800ff425 also KEY_GREEN on some receivers */ + { 0x800ff425, KEY_TUNER }, /* LiveTV */ + { 0x800ff450, KEY_RADIO }, + + { 0x800ff44c, KEY_LANGUAGE }, + { 0x800ff427, KEY_ZOOM }, /* Aspect */ + + { 0x800ff45b, KEY_RED }, + { 0x800ff45c, KEY_GREEN }, + { 0x800ff45d, KEY_YELLOW }, + { 0x800ff45e, KEY_BLUE }, + + { 0x800ff466, KEY_RED }, + /* { 0x800ff425, KEY_GREEN }, */ + { 0x800ff468, KEY_YELLOW }, + /* { 0x800ff41d, KEY_BLUE }, */ + + { 0x800ff40f, KEY_INFO }, + { 0x800ff426, KEY_EPG }, /* Guide */ + { 0x800ff45a, KEY_SUBTITLE }, /* Caption/Teletext */ + { 0x800ff44d, KEY_TITLE }, + + { 0x800ff40c, KEY_POWER }, + { 0x800ff40d, KEY_PROG1 }, /* Windows MCE button */ + +}; + +static struct rc_keymap imon_mce_map = { + .map = { + .scan = imon_mce, + .size = ARRAY_SIZE(imon_mce), + /* its RC6, but w/a hardware decoder */ + .ir_type = IR_TYPE_RC6, + .name = RC_MAP_IMON_MCE, + } +}; + +static int __init init_rc_map_imon_mce(void) +{ + return ir_register_map(&imon_mce_map); +} + +static void __exit exit_rc_map_imon_mce(void) +{ + ir_unregister_map(&imon_mce_map); +} + +module_init(init_rc_map_imon_mce) +module_exit(exit_rc_map_imon_mce) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-imon-pad.c b/drivers/media/IR/keymaps/rc-imon-pad.c new file mode 100644 index 000000000000..bc4db72f02e6 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-imon-pad.c @@ -0,0 +1,156 @@ +/* rc5-imon-pad.c - Keytable for SoundGraph iMON PAD and Antec Veris + * RM-200 Remote Control + * + * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + * standard imon remote key table, which isn't really entirely + * "standard", as different receivers decode the same key on the + * same remote to different hex codes, and the silkscreened names + * vary a bit between the SoundGraph and Antec remotes... ugh. + */ +static struct ir_scancode imon_pad[] = { + /* keys sorted mostly by frequency of use to optimize lookups */ + { 0x2a8195b7, KEY_REWIND }, + { 0x298315b7, KEY_REWIND }, + { 0x2b8115b7, KEY_FASTFORWARD }, + { 0x2b8315b7, KEY_FASTFORWARD }, + { 0x2b9115b7, KEY_PREVIOUS }, + { 0x298195b7, KEY_NEXT }, + + { 0x2a8115b7, KEY_PLAY }, + { 0x2a8315b7, KEY_PLAY }, + { 0x2a9115b7, KEY_PAUSE }, + { 0x2b9715b7, KEY_STOP }, + { 0x298115b7, KEY_RECORD }, + + { 0x01008000, KEY_UP }, + { 0x01007f00, KEY_DOWN }, + { 0x01000080, KEY_LEFT }, + { 0x0100007f, KEY_RIGHT }, + + { 0x2aa515b7, KEY_UP }, + { 0x289515b7, KEY_DOWN }, + { 0x29a515b7, KEY_LEFT }, + { 0x2ba515b7, KEY_RIGHT }, + + { 0x0200002c, KEY_SPACE }, /* Select/Space */ + { 0x2a9315b7, KEY_SPACE }, /* Select/Space */ + { 0x02000028, KEY_ENTER }, + { 0x28a195b7, KEY_ENTER }, + { 0x288195b7, KEY_EXIT }, + { 0x02000029, KEY_ESC }, + { 0x2bb715b7, KEY_ESC }, + { 0x0200002a, KEY_BACKSPACE }, + { 0x28a115b7, KEY_BACKSPACE }, + + { 0x2b9595b7, KEY_MUTE }, + { 0x28a395b7, KEY_VOLUMEUP }, + { 0x28a595b7, KEY_VOLUMEDOWN }, + { 0x289395b7, KEY_CHANNELUP }, + { 0x288795b7, KEY_CHANNELDOWN }, + + { 0x0200001e, KEY_NUMERIC_1 }, + { 0x0200001f, KEY_NUMERIC_2 }, + { 0x02000020, KEY_NUMERIC_3 }, + { 0x02000021, KEY_NUMERIC_4 }, + { 0x02000022, KEY_NUMERIC_5 }, + { 0x02000023, KEY_NUMERIC_6 }, + { 0x02000024, KEY_NUMERIC_7 }, + { 0x02000025, KEY_NUMERIC_8 }, + { 0x02000026, KEY_NUMERIC_9 }, + { 0x02000027, KEY_NUMERIC_0 }, + + { 0x28b595b7, KEY_NUMERIC_1 }, + { 0x2bb195b7, KEY_NUMERIC_2 }, + { 0x28b195b7, KEY_NUMERIC_3 }, + { 0x2a8595b7, KEY_NUMERIC_4 }, + { 0x299595b7, KEY_NUMERIC_5 }, + { 0x2aa595b7, KEY_NUMERIC_6 }, + { 0x2b9395b7, KEY_NUMERIC_7 }, + { 0x2a8515b7, KEY_NUMERIC_8 }, + { 0x2aa115b7, KEY_NUMERIC_9 }, + { 0x2ba595b7, KEY_NUMERIC_0 }, + + { 0x02200025, KEY_NUMERIC_STAR }, + { 0x28b515b7, KEY_NUMERIC_STAR }, + { 0x02200020, KEY_NUMERIC_POUND }, + { 0x29a115b7, KEY_NUMERIC_POUND }, + + { 0x2b8515b7, KEY_VIDEO }, + { 0x299195b7, KEY_AUDIO }, + { 0x2ba115b7, KEY_CAMERA }, + { 0x28a515b7, KEY_TV }, + { 0x29a395b7, KEY_DVD }, + { 0x29a295b7, KEY_DVD }, + + /* the Menu key between DVD and Subtitle on the RM-200... */ + { 0x2ba385b7, KEY_MENU }, + { 0x2ba395b7, KEY_MENU }, + + { 0x288515b7, KEY_BOOKMARKS }, + { 0x2ab715b7, KEY_MEDIA }, /* Thumbnail */ + { 0x298595b7, KEY_SUBTITLE }, + { 0x2b8595b7, KEY_LANGUAGE }, + + { 0x29a595b7, KEY_ZOOM }, + { 0x2aa395b7, KEY_SCREEN }, /* FullScreen */ + + { 0x299115b7, KEY_KEYBOARD }, + { 0x299135b7, KEY_KEYBOARD }, + + { 0x01010000, BTN_LEFT }, + { 0x01020000, BTN_RIGHT }, + { 0x01010080, BTN_LEFT }, + { 0x01020080, BTN_RIGHT }, + { 0x688301b7, BTN_LEFT }, + { 0x688481b7, BTN_RIGHT }, + + { 0x2a9395b7, KEY_CYCLEWINDOWS }, /* TaskSwitcher */ + { 0x2b8395b7, KEY_TIME }, /* Timer */ + + { 0x289115b7, KEY_POWER }, + { 0x29b195b7, KEY_EJECTCD }, /* the one next to play */ + { 0x299395b7, KEY_EJECTCLOSECD }, /* eject (by TaskSw) */ + + { 0x02800000, KEY_CONTEXT_MENU }, /* Left Menu */ + { 0x2b8195b7, KEY_CONTEXT_MENU }, /* Left Menu*/ + { 0x02000065, KEY_COMPOSE }, /* RightMenu */ + { 0x28b715b7, KEY_COMPOSE }, /* RightMenu */ + { 0x2ab195b7, KEY_PROG1 }, /* Go or MultiMon */ + { 0x29b715b7, KEY_DASHBOARD }, /* AppLauncher */ +}; + +static struct rc_keymap imon_pad_map = { + .map = { + .scan = imon_pad, + .size = ARRAY_SIZE(imon_pad), + /* actual protocol details unknown, hardware decoder */ + .ir_type = IR_TYPE_OTHER, + .name = RC_MAP_IMON_PAD, + } +}; + +static int __init init_rc_map_imon_pad(void) +{ + return ir_register_map(&imon_pad_map); +} + +static void __exit exit_rc_map_imon_pad(void) +{ + ir_unregister_map(&imon_pad_map); +} + +module_init(init_rc_map_imon_pad) +module_exit(exit_rc_map_imon_pad) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-iodata-bctv7e.c b/drivers/media/IR/keymaps/rc-iodata-bctv7e.c new file mode 100644 index 000000000000..ef6600259fc0 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-iodata-bctv7e.c @@ -0,0 +1,88 @@ +/* iodata-bctv7e.h - Keytable for iodata_bctv7e Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* IO-DATA BCTV7E Remote */ + +static struct ir_scancode iodata_bctv7e[] = { + { 0x40, KEY_TV }, + { 0x20, KEY_RADIO }, /* FM */ + { 0x60, KEY_EPG }, + { 0x00, KEY_POWER }, + + /* Keys 0 to 9 */ + { 0x44, KEY_0 }, /* 10 */ + { 0x50, KEY_1 }, + { 0x30, KEY_2 }, + { 0x70, KEY_3 }, + { 0x48, KEY_4 }, + { 0x28, KEY_5 }, + { 0x68, KEY_6 }, + { 0x58, KEY_7 }, + { 0x38, KEY_8 }, + { 0x78, KEY_9 }, + + { 0x10, KEY_L }, /* Live */ + { 0x08, KEY_TIME }, /* Time Shift */ + + { 0x18, KEY_PLAYPAUSE }, /* Play */ + + { 0x24, KEY_ENTER }, /* 11 */ + { 0x64, KEY_ESC }, /* 12 */ + { 0x04, KEY_M }, /* Multi */ + + { 0x54, KEY_VIDEO }, + { 0x34, KEY_CHANNELUP }, + { 0x74, KEY_VOLUMEUP }, + { 0x14, KEY_MUTE }, + + { 0x4c, KEY_VCR }, /* SVIDEO */ + { 0x2c, KEY_CHANNELDOWN }, + { 0x6c, KEY_VOLUMEDOWN }, + { 0x0c, KEY_ZOOM }, + + { 0x5c, KEY_PAUSE }, + { 0x3c, KEY_RED }, /* || (red) */ + { 0x7c, KEY_RECORD }, /* recording */ + { 0x1c, KEY_STOP }, + + { 0x41, KEY_REWIND }, /* backward << */ + { 0x21, KEY_PLAY }, + { 0x61, KEY_FASTFORWARD }, /* forward >> */ + { 0x01, KEY_NEXT }, /* skip >| */ +}; + +static struct rc_keymap iodata_bctv7e_map = { + .map = { + .scan = iodata_bctv7e, + .size = ARRAY_SIZE(iodata_bctv7e), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_IODATA_BCTV7E, + } +}; + +static int __init init_rc_map_iodata_bctv7e(void) +{ + return ir_register_map(&iodata_bctv7e_map); +} + +static void __exit exit_rc_map_iodata_bctv7e(void) +{ + ir_unregister_map(&iodata_bctv7e_map); +} + +module_init(init_rc_map_iodata_bctv7e) +module_exit(exit_rc_map_iodata_bctv7e) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-kaiomy.c b/drivers/media/IR/keymaps/rc-kaiomy.c new file mode 100644 index 000000000000..4c7883ba0f15 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-kaiomy.c @@ -0,0 +1,87 @@ +/* kaiomy.h - Keytable for kaiomy Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Kaiomy TVnPC U2 + Mauro Carvalho Chehab <mchehab@infradead.org> + */ + +static struct ir_scancode kaiomy[] = { + { 0x43, KEY_POWER2}, + { 0x01, KEY_LIST}, + { 0x0b, KEY_ZOOM}, + { 0x03, KEY_POWER}, + + { 0x04, KEY_1}, + { 0x08, KEY_2}, + { 0x02, KEY_3}, + + { 0x0f, KEY_4}, + { 0x05, KEY_5}, + { 0x06, KEY_6}, + + { 0x0c, KEY_7}, + { 0x0d, KEY_8}, + { 0x0a, KEY_9}, + + { 0x11, KEY_0}, + + { 0x09, KEY_CHANNELUP}, + { 0x07, KEY_CHANNELDOWN}, + + { 0x0e, KEY_VOLUMEUP}, + { 0x13, KEY_VOLUMEDOWN}, + + { 0x10, KEY_HOME}, + { 0x12, KEY_ENTER}, + + { 0x14, KEY_RECORD}, + { 0x15, KEY_STOP}, + { 0x16, KEY_PLAY}, + { 0x17, KEY_MUTE}, + + { 0x18, KEY_UP}, + { 0x19, KEY_DOWN}, + { 0x1a, KEY_LEFT}, + { 0x1b, KEY_RIGHT}, + + { 0x1c, KEY_RED}, + { 0x1d, KEY_GREEN}, + { 0x1e, KEY_YELLOW}, + { 0x1f, KEY_BLUE}, +}; + +static struct rc_keymap kaiomy_map = { + .map = { + .scan = kaiomy, + .size = ARRAY_SIZE(kaiomy), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_KAIOMY, + } +}; + +static int __init init_rc_map_kaiomy(void) +{ + return ir_register_map(&kaiomy_map); +} + +static void __exit exit_rc_map_kaiomy(void) +{ + ir_unregister_map(&kaiomy_map); +} + +module_init(init_rc_map_kaiomy) +module_exit(exit_rc_map_kaiomy) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-kworld-315u.c b/drivers/media/IR/keymaps/rc-kworld-315u.c new file mode 100644 index 000000000000..618c817374e6 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-kworld-315u.c @@ -0,0 +1,83 @@ +/* kworld-315u.h - Keytable for kworld_315u Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Kworld 315U + */ + +static struct ir_scancode kworld_315u[] = { + { 0x6143, KEY_POWER }, + { 0x6101, KEY_TUNER }, /* source */ + { 0x610b, KEY_ZOOM }, + { 0x6103, KEY_POWER2 }, /* shutdown */ + + { 0x6104, KEY_1 }, + { 0x6108, KEY_2 }, + { 0x6102, KEY_3 }, + { 0x6109, KEY_CHANNELUP }, + + { 0x610f, KEY_4 }, + { 0x6105, KEY_5 }, + { 0x6106, KEY_6 }, + { 0x6107, KEY_CHANNELDOWN }, + + { 0x610c, KEY_7 }, + { 0x610d, KEY_8 }, + { 0x610a, KEY_9 }, + { 0x610e, KEY_VOLUMEUP }, + + { 0x6110, KEY_LAST }, + { 0x6111, KEY_0 }, + { 0x6112, KEY_ENTER }, + { 0x6113, KEY_VOLUMEDOWN }, + + { 0x6114, KEY_RECORD }, + { 0x6115, KEY_STOP }, + { 0x6116, KEY_PLAY }, + { 0x6117, KEY_MUTE }, + + { 0x6118, KEY_UP }, + { 0x6119, KEY_DOWN }, + { 0x611a, KEY_LEFT }, + { 0x611b, KEY_RIGHT }, + + { 0x611c, KEY_RED }, + { 0x611d, KEY_GREEN }, + { 0x611e, KEY_YELLOW }, + { 0x611f, KEY_BLUE }, +}; + +static struct rc_keymap kworld_315u_map = { + .map = { + .scan = kworld_315u, + .size = ARRAY_SIZE(kworld_315u), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_KWORLD_315U, + } +}; + +static int __init init_rc_map_kworld_315u(void) +{ + return ir_register_map(&kworld_315u_map); +} + +static void __exit exit_rc_map_kworld_315u(void) +{ + ir_unregister_map(&kworld_315u_map); +} + +module_init(init_rc_map_kworld_315u) +module_exit(exit_rc_map_kworld_315u) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-kworld-plus-tv-analog.c b/drivers/media/IR/keymaps/rc-kworld-plus-tv-analog.c new file mode 100644 index 000000000000..366732f1f7b7 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-kworld-plus-tv-analog.c @@ -0,0 +1,99 @@ +/* kworld-plus-tv-analog.h - Keytable for kworld_plus_tv_analog Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Kworld Plus TV Analog Lite PCI IR + Mauro Carvalho Chehab <mchehab@infradead.org> + */ + +static struct ir_scancode kworld_plus_tv_analog[] = { + { 0x0c, KEY_PROG1 }, /* Kworld key */ + { 0x16, KEY_CLOSECD }, /* -> ) */ + { 0x1d, KEY_POWER2 }, + + { 0x00, KEY_1 }, + { 0x01, KEY_2 }, + { 0x02, KEY_3 }, /* Two keys have the same code: 3 and left */ + { 0x03, KEY_4 }, /* Two keys have the same code: 3 and right */ + { 0x04, KEY_5 }, + { 0x05, KEY_6 }, + { 0x06, KEY_7 }, + { 0x07, KEY_8 }, + { 0x08, KEY_9 }, + { 0x0a, KEY_0 }, + + { 0x09, KEY_AGAIN }, + { 0x14, KEY_MUTE }, + + { 0x20, KEY_UP }, + { 0x21, KEY_DOWN }, + { 0x0b, KEY_ENTER }, + + { 0x10, KEY_CHANNELUP }, + { 0x11, KEY_CHANNELDOWN }, + + /* Couldn't map key left/key right since those + conflict with '3' and '4' scancodes + I dunno what the original driver does + */ + + { 0x13, KEY_VOLUMEUP }, + { 0x12, KEY_VOLUMEDOWN }, + + /* The lower part of the IR + There are several duplicated keycodes there. + Most of them conflict with digits. + Add mappings just to the unused scancodes. + Somehow, the original driver has a way to know, + but this doesn't seem to be on some GPIO. + Also, it is not related to the time between keyup + and keydown. + */ + { 0x19, KEY_TIME}, /* Timeshift */ + { 0x1a, KEY_STOP}, + { 0x1b, KEY_RECORD}, + + { 0x22, KEY_TEXT}, + + { 0x15, KEY_AUDIO}, /* ((*)) */ + { 0x0f, KEY_ZOOM}, + { 0x1c, KEY_CAMERA}, /* snapshot */ + + { 0x18, KEY_RED}, /* B */ + { 0x23, KEY_GREEN}, /* C */ +}; + +static struct rc_keymap kworld_plus_tv_analog_map = { + .map = { + .scan = kworld_plus_tv_analog, + .size = ARRAY_SIZE(kworld_plus_tv_analog), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_KWORLD_PLUS_TV_ANALOG, + } +}; + +static int __init init_rc_map_kworld_plus_tv_analog(void) +{ + return ir_register_map(&kworld_plus_tv_analog_map); +} + +static void __exit exit_rc_map_kworld_plus_tv_analog(void) +{ + ir_unregister_map(&kworld_plus_tv_analog_map); +} + +module_init(init_rc_map_kworld_plus_tv_analog) +module_exit(exit_rc_map_kworld_plus_tv_analog) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-manli.c b/drivers/media/IR/keymaps/rc-manli.c new file mode 100644 index 000000000000..1e9fbfa90a1e --- /dev/null +++ b/drivers/media/IR/keymaps/rc-manli.c @@ -0,0 +1,135 @@ +/* manli.h - Keytable for manli Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Michael Tokarev <mjt@tls.msk.ru> + http://www.corpit.ru/mjt/beholdTV/remote_control.jpg + keytable is used by MANLI MTV00[0x0c] and BeholdTV 40[13] at + least, and probably other cards too. + The "ascii-art picture" below (in comments, first row + is the keycode in hex, and subsequent row(s) shows + the button labels (several variants when appropriate) + helps to descide which keycodes to assign to the buttons. + */ + +static struct ir_scancode manli[] = { + + /* 0x1c 0x12 * + * FUNCTION POWER * + * FM (|) * + * */ + { 0x1c, KEY_RADIO }, /*XXX*/ + { 0x12, KEY_POWER }, + + /* 0x01 0x02 0x03 * + * 1 2 3 * + * * + * 0x04 0x05 0x06 * + * 4 5 6 * + * * + * 0x07 0x08 0x09 * + * 7 8 9 * + * */ + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + /* 0x0a 0x00 0x17 * + * RECALL 0 +100 * + * PLUS * + * */ + { 0x0a, KEY_AGAIN }, /*XXX KEY_REWIND? */ + { 0x00, KEY_0 }, + { 0x17, KEY_DIGITS }, /*XXX*/ + + /* 0x14 0x10 * + * MENU INFO * + * OSD */ + { 0x14, KEY_MENU }, + { 0x10, KEY_INFO }, + + /* 0x0b * + * Up * + * * + * 0x18 0x16 0x0c * + * Left Ok Right * + * * + * 0x015 * + * Down * + * */ + { 0x0b, KEY_UP }, + { 0x18, KEY_LEFT }, + { 0x16, KEY_OK }, /*XXX KEY_SELECT? KEY_ENTER? */ + { 0x0c, KEY_RIGHT }, + { 0x15, KEY_DOWN }, + + /* 0x11 0x0d * + * TV/AV MODE * + * SOURCE STEREO * + * */ + { 0x11, KEY_TV }, /*XXX*/ + { 0x0d, KEY_MODE }, /*XXX there's no KEY_STEREO */ + + /* 0x0f 0x1b 0x1a * + * AUDIO Vol+ Chan+ * + * TIMESHIFT??? * + * * + * 0x0e 0x1f 0x1e * + * SLEEP Vol- Chan- * + * */ + { 0x0f, KEY_AUDIO }, + { 0x1b, KEY_VOLUMEUP }, + { 0x1a, KEY_CHANNELUP }, + { 0x0e, KEY_TIME }, + { 0x1f, KEY_VOLUMEDOWN }, + { 0x1e, KEY_CHANNELDOWN }, + + /* 0x13 0x19 * + * MUTE SNAPSHOT* + * */ + { 0x13, KEY_MUTE }, + { 0x19, KEY_CAMERA }, + + /* 0x1d unused ? */ +}; + +static struct rc_keymap manli_map = { + .map = { + .scan = manli, + .size = ARRAY_SIZE(manli), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_MANLI, + } +}; + +static int __init init_rc_map_manli(void) +{ + return ir_register_map(&manli_map); +} + +static void __exit exit_rc_map_manli(void) +{ + ir_unregister_map(&manli_map); +} + +module_init(init_rc_map_manli) +module_exit(exit_rc_map_manli) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-msi-tvanywhere-plus.c b/drivers/media/IR/keymaps/rc-msi-tvanywhere-plus.c new file mode 100644 index 000000000000..eb8e42c18ff9 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-msi-tvanywhere-plus.c @@ -0,0 +1,123 @@ +/* msi-tvanywhere-plus.h - Keytable for msi_tvanywhere_plus Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + Keycodes for remote on the MSI TV@nywhere Plus. The controller IC on the card + is marked "KS003". The controller is I2C at address 0x30, but does not seem + to respond to probes until a read is performed from a valid device. + I don't know why... + + Note: This remote may be of similar or identical design to the + Pixelview remote (?). The raw codes and duplicate button codes + appear to be the same. + + Henry Wong <henry@stuffedcow.net> + Some changes to formatting and keycodes by Mark Schultz <n9xmj@yahoo.com> +*/ + +static struct ir_scancode msi_tvanywhere_plus[] = { + +/* ---- Remote Button Layout ---- + + POWER SOURCE SCAN MUTE + TV/FM 1 2 3 + |> 4 5 6 + <| 7 8 9 + ^^UP 0 + RECALL + vvDN RECORD STOP PLAY + + MINIMIZE ZOOM + + CH+ + VOL- VOL+ + CH- + + SNAPSHOT MTS + + << FUNC >> RESET +*/ + + { 0x01, KEY_1 }, /* 1 */ + { 0x0b, KEY_2 }, /* 2 */ + { 0x1b, KEY_3 }, /* 3 */ + { 0x05, KEY_4 }, /* 4 */ + { 0x09, KEY_5 }, /* 5 */ + { 0x15, KEY_6 }, /* 6 */ + { 0x06, KEY_7 }, /* 7 */ + { 0x0a, KEY_8 }, /* 8 */ + { 0x12, KEY_9 }, /* 9 */ + { 0x02, KEY_0 }, /* 0 */ + { 0x10, KEY_KPPLUS }, /* + */ + { 0x13, KEY_AGAIN }, /* Recall */ + + { 0x1e, KEY_POWER }, /* Power */ + { 0x07, KEY_TUNER }, /* Source */ + { 0x1c, KEY_SEARCH }, /* Scan */ + { 0x18, KEY_MUTE }, /* Mute */ + + { 0x03, KEY_RADIO }, /* TV/FM */ + /* The next four keys are duplicates that appear to send the + same IR code as Ch+, Ch-, >>, and << . The raw code assigned + to them is the actual code + 0x20 - they will never be + detected as such unless some way is discovered to distinguish + these buttons from those that have the same code. */ + { 0x3f, KEY_RIGHT }, /* |> and Ch+ */ + { 0x37, KEY_LEFT }, /* <| and Ch- */ + { 0x2c, KEY_UP }, /* ^^Up and >> */ + { 0x24, KEY_DOWN }, /* vvDn and << */ + + { 0x00, KEY_RECORD }, /* Record */ + { 0x08, KEY_STOP }, /* Stop */ + { 0x11, KEY_PLAY }, /* Play */ + + { 0x0f, KEY_CLOSE }, /* Minimize */ + { 0x19, KEY_ZOOM }, /* Zoom */ + { 0x1a, KEY_CAMERA }, /* Snapshot */ + { 0x0d, KEY_LANGUAGE }, /* MTS */ + + { 0x14, KEY_VOLUMEDOWN }, /* Vol- */ + { 0x16, KEY_VOLUMEUP }, /* Vol+ */ + { 0x17, KEY_CHANNELDOWN }, /* Ch- */ + { 0x1f, KEY_CHANNELUP }, /* Ch+ */ + + { 0x04, KEY_REWIND }, /* << */ + { 0x0e, KEY_MENU }, /* Function */ + { 0x0c, KEY_FASTFORWARD }, /* >> */ + { 0x1d, KEY_RESTART }, /* Reset */ +}; + +static struct rc_keymap msi_tvanywhere_plus_map = { + .map = { + .scan = msi_tvanywhere_plus, + .size = ARRAY_SIZE(msi_tvanywhere_plus), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_MSI_TVANYWHERE_PLUS, + } +}; + +static int __init init_rc_map_msi_tvanywhere_plus(void) +{ + return ir_register_map(&msi_tvanywhere_plus_map); +} + +static void __exit exit_rc_map_msi_tvanywhere_plus(void) +{ + ir_unregister_map(&msi_tvanywhere_plus_map); +} + +module_init(init_rc_map_msi_tvanywhere_plus) +module_exit(exit_rc_map_msi_tvanywhere_plus) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-msi-tvanywhere.c b/drivers/media/IR/keymaps/rc-msi-tvanywhere.c new file mode 100644 index 000000000000..ef411854f067 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-msi-tvanywhere.c @@ -0,0 +1,69 @@ +/* msi-tvanywhere.h - Keytable for msi_tvanywhere Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* MSI TV@nywhere MASTER remote */ + +static struct ir_scancode msi_tvanywhere[] = { + /* Keys 0 to 9 */ + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0c, KEY_MUTE }, + { 0x0f, KEY_SCREEN }, /* Full Screen */ + { 0x10, KEY_FN }, /* Funtion */ + { 0x11, KEY_TIME }, /* Time shift */ + { 0x12, KEY_POWER }, + { 0x13, KEY_MEDIA }, /* MTS */ + { 0x14, KEY_SLOW }, + { 0x16, KEY_REWIND }, /* backward << */ + { 0x17, KEY_ENTER }, /* Return */ + { 0x18, KEY_FASTFORWARD }, /* forward >> */ + { 0x1a, KEY_CHANNELUP }, + { 0x1b, KEY_VOLUMEUP }, + { 0x1e, KEY_CHANNELDOWN }, + { 0x1f, KEY_VOLUMEDOWN }, +}; + +static struct rc_keymap msi_tvanywhere_map = { + .map = { + .scan = msi_tvanywhere, + .size = ARRAY_SIZE(msi_tvanywhere), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_MSI_TVANYWHERE, + } +}; + +static int __init init_rc_map_msi_tvanywhere(void) +{ + return ir_register_map(&msi_tvanywhere_map); +} + +static void __exit exit_rc_map_msi_tvanywhere(void) +{ + ir_unregister_map(&msi_tvanywhere_map); +} + +module_init(init_rc_map_msi_tvanywhere) +module_exit(exit_rc_map_msi_tvanywhere) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-nebula.c b/drivers/media/IR/keymaps/rc-nebula.c new file mode 100644 index 000000000000..ccc50eb402ec --- /dev/null +++ b/drivers/media/IR/keymaps/rc-nebula.c @@ -0,0 +1,96 @@ +/* nebula.h - Keytable for nebula Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode nebula[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0a, KEY_TV }, + { 0x0b, KEY_AUX }, + { 0x0c, KEY_DVD }, + { 0x0d, KEY_POWER }, + { 0x0e, KEY_MHP }, /* labelled 'Picture' */ + { 0x0f, KEY_AUDIO }, + { 0x10, KEY_INFO }, + { 0x11, KEY_F13 }, /* 16:9 */ + { 0x12, KEY_F14 }, /* 14:9 */ + { 0x13, KEY_EPG }, + { 0x14, KEY_EXIT }, + { 0x15, KEY_MENU }, + { 0x16, KEY_UP }, + { 0x17, KEY_DOWN }, + { 0x18, KEY_LEFT }, + { 0x19, KEY_RIGHT }, + { 0x1a, KEY_ENTER }, + { 0x1b, KEY_CHANNELUP }, + { 0x1c, KEY_CHANNELDOWN }, + { 0x1d, KEY_VOLUMEUP }, + { 0x1e, KEY_VOLUMEDOWN }, + { 0x1f, KEY_RED }, + { 0x20, KEY_GREEN }, + { 0x21, KEY_YELLOW }, + { 0x22, KEY_BLUE }, + { 0x23, KEY_SUBTITLE }, + { 0x24, KEY_F15 }, /* AD */ + { 0x25, KEY_TEXT }, + { 0x26, KEY_MUTE }, + { 0x27, KEY_REWIND }, + { 0x28, KEY_STOP }, + { 0x29, KEY_PLAY }, + { 0x2a, KEY_FASTFORWARD }, + { 0x2b, KEY_F16 }, /* chapter */ + { 0x2c, KEY_PAUSE }, + { 0x2d, KEY_PLAY }, + { 0x2e, KEY_RECORD }, + { 0x2f, KEY_F17 }, /* picture in picture */ + { 0x30, KEY_KPPLUS }, /* zoom in */ + { 0x31, KEY_KPMINUS }, /* zoom out */ + { 0x32, KEY_F18 }, /* capture */ + { 0x33, KEY_F19 }, /* web */ + { 0x34, KEY_EMAIL }, + { 0x35, KEY_PHONE }, + { 0x36, KEY_PC }, +}; + +static struct rc_keymap nebula_map = { + .map = { + .scan = nebula, + .size = ARRAY_SIZE(nebula), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_NEBULA, + } +}; + +static int __init init_rc_map_nebula(void) +{ + return ir_register_map(&nebula_map); +} + +static void __exit exit_rc_map_nebula(void) +{ + ir_unregister_map(&nebula_map); +} + +module_init(init_rc_map_nebula) +module_exit(exit_rc_map_nebula) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-nec-terratec-cinergy-xs.c b/drivers/media/IR/keymaps/rc-nec-terratec-cinergy-xs.c new file mode 100644 index 000000000000..e1b54d20db60 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-nec-terratec-cinergy-xs.c @@ -0,0 +1,105 @@ +/* nec-terratec-cinergy-xs.h - Keytable for nec_terratec_cinergy_xs Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Terratec Cinergy Hybrid T USB XS FM + Mauro Carvalho Chehab <mchehab@redhat.com> + */ + +static struct ir_scancode nec_terratec_cinergy_xs[] = { + { 0x1441, KEY_HOME}, + { 0x1401, KEY_POWER2}, + + { 0x1442, KEY_MENU}, /* DVD menu */ + { 0x1443, KEY_SUBTITLE}, + { 0x1444, KEY_TEXT}, /* Teletext */ + { 0x1445, KEY_DELETE}, + + { 0x1402, KEY_1}, + { 0x1403, KEY_2}, + { 0x1404, KEY_3}, + { 0x1405, KEY_4}, + { 0x1406, KEY_5}, + { 0x1407, KEY_6}, + { 0x1408, KEY_7}, + { 0x1409, KEY_8}, + { 0x140a, KEY_9}, + { 0x140c, KEY_0}, + + { 0x140b, KEY_TUNER}, /* AV */ + { 0x140d, KEY_MODE}, /* A.B */ + + { 0x1446, KEY_TV}, + { 0x1447, KEY_DVD}, + { 0x1449, KEY_VIDEO}, + { 0x144a, KEY_RADIO}, /* Music */ + { 0x144b, KEY_CAMERA}, /* PIC */ + + { 0x1410, KEY_UP}, + { 0x1411, KEY_LEFT}, + { 0x1412, KEY_OK}, + { 0x1413, KEY_RIGHT}, + { 0x1414, KEY_DOWN}, + + { 0x140f, KEY_EPG}, + { 0x1416, KEY_INFO}, + { 0x144d, KEY_BACKSPACE}, + + { 0x141c, KEY_VOLUMEUP}, + { 0x141e, KEY_VOLUMEDOWN}, + + { 0x144c, KEY_PLAY}, + { 0x141d, KEY_MUTE}, + + { 0x141b, KEY_CHANNELUP}, + { 0x141f, KEY_CHANNELDOWN}, + + { 0x1417, KEY_RED}, + { 0x1418, KEY_GREEN}, + { 0x1419, KEY_YELLOW}, + { 0x141a, KEY_BLUE}, + + { 0x1458, KEY_RECORD}, + { 0x1448, KEY_STOP}, + { 0x1440, KEY_PAUSE}, + + { 0x1454, KEY_LAST}, + { 0x144e, KEY_REWIND}, + { 0x144f, KEY_FASTFORWARD}, + { 0x145c, KEY_NEXT}, +}; + +static struct rc_keymap nec_terratec_cinergy_xs_map = { + .map = { + .scan = nec_terratec_cinergy_xs, + .size = ARRAY_SIZE(nec_terratec_cinergy_xs), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_NEC_TERRATEC_CINERGY_XS, + } +}; + +static int __init init_rc_map_nec_terratec_cinergy_xs(void) +{ + return ir_register_map(&nec_terratec_cinergy_xs_map); +} + +static void __exit exit_rc_map_nec_terratec_cinergy_xs(void) +{ + ir_unregister_map(&nec_terratec_cinergy_xs_map); +} + +module_init(init_rc_map_nec_terratec_cinergy_xs) +module_exit(exit_rc_map_nec_terratec_cinergy_xs) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-norwood.c b/drivers/media/IR/keymaps/rc-norwood.c new file mode 100644 index 000000000000..e5849a6b3f05 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-norwood.c @@ -0,0 +1,85 @@ +/* norwood.h - Keytable for norwood Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Norwood Micro (non-Pro) TV Tuner + By Peter Naulls <peter@chocky.org> + Key comments are the functions given in the manual */ + +static struct ir_scancode norwood[] = { + /* Keys 0 to 9 */ + { 0x20, KEY_0 }, + { 0x21, KEY_1 }, + { 0x22, KEY_2 }, + { 0x23, KEY_3 }, + { 0x24, KEY_4 }, + { 0x25, KEY_5 }, + { 0x26, KEY_6 }, + { 0x27, KEY_7 }, + { 0x28, KEY_8 }, + { 0x29, KEY_9 }, + + { 0x78, KEY_TUNER }, /* Video Source */ + { 0x2c, KEY_EXIT }, /* Open/Close software */ + { 0x2a, KEY_SELECT }, /* 2 Digit Select */ + { 0x69, KEY_AGAIN }, /* Recall */ + + { 0x32, KEY_BRIGHTNESSUP }, /* Brightness increase */ + { 0x33, KEY_BRIGHTNESSDOWN }, /* Brightness decrease */ + { 0x6b, KEY_KPPLUS }, /* (not named >>>>>) */ + { 0x6c, KEY_KPMINUS }, /* (not named <<<<<) */ + + { 0x2d, KEY_MUTE }, /* Mute */ + { 0x30, KEY_VOLUMEUP }, /* Volume up */ + { 0x31, KEY_VOLUMEDOWN }, /* Volume down */ + { 0x60, KEY_CHANNELUP }, /* Channel up */ + { 0x61, KEY_CHANNELDOWN }, /* Channel down */ + + { 0x3f, KEY_RECORD }, /* Record */ + { 0x37, KEY_PLAY }, /* Play */ + { 0x36, KEY_PAUSE }, /* Pause */ + { 0x2b, KEY_STOP }, /* Stop */ + { 0x67, KEY_FASTFORWARD }, /* Foward */ + { 0x66, KEY_REWIND }, /* Rewind */ + { 0x3e, KEY_SEARCH }, /* Auto Scan */ + { 0x2e, KEY_CAMERA }, /* Capture Video */ + { 0x6d, KEY_MENU }, /* Show/Hide Control */ + { 0x2f, KEY_ZOOM }, /* Full Screen */ + { 0x34, KEY_RADIO }, /* FM */ + { 0x65, KEY_POWER }, /* Computer power */ +}; + +static struct rc_keymap norwood_map = { + .map = { + .scan = norwood, + .size = ARRAY_SIZE(norwood), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_NORWOOD, + } +}; + +static int __init init_rc_map_norwood(void) +{ + return ir_register_map(&norwood_map); +} + +static void __exit exit_rc_map_norwood(void) +{ + ir_unregister_map(&norwood_map); +} + +module_init(init_rc_map_norwood) +module_exit(exit_rc_map_norwood) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-npgtech.c b/drivers/media/IR/keymaps/rc-npgtech.c new file mode 100644 index 000000000000..b9ece1e90296 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-npgtech.c @@ -0,0 +1,80 @@ +/* npgtech.h - Keytable for npgtech Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode npgtech[] = { + { 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */ + { 0x2a, KEY_FRONT }, + + { 0x3e, KEY_1 }, + { 0x02, KEY_2 }, + { 0x06, KEY_3 }, + { 0x0a, KEY_4 }, + { 0x0e, KEY_5 }, + { 0x12, KEY_6 }, + { 0x16, KEY_7 }, + { 0x1a, KEY_8 }, + { 0x1e, KEY_9 }, + { 0x3a, KEY_0 }, + { 0x22, KEY_NUMLOCK }, /* -/-- */ + { 0x20, KEY_REFRESH }, + + { 0x03, KEY_BRIGHTNESSDOWN }, + { 0x28, KEY_AUDIO }, + { 0x3c, KEY_CHANNELUP }, + { 0x3f, KEY_VOLUMEDOWN }, + { 0x2e, KEY_MUTE }, + { 0x3b, KEY_VOLUMEUP }, + { 0x00, KEY_CHANNELDOWN }, + { 0x07, KEY_BRIGHTNESSUP }, + { 0x2c, KEY_TEXT }, + + { 0x37, KEY_RECORD }, + { 0x17, KEY_PLAY }, + { 0x13, KEY_PAUSE }, + { 0x26, KEY_STOP }, + { 0x18, KEY_FASTFORWARD }, + { 0x14, KEY_REWIND }, + { 0x33, KEY_ZOOM }, + { 0x32, KEY_KEYBOARD }, + { 0x30, KEY_GOTO }, /* Pointing arrow */ + { 0x36, KEY_MACRO }, /* Maximize/Minimize (yellow) */ + { 0x0b, KEY_RADIO }, + { 0x10, KEY_POWER }, + +}; + +static struct rc_keymap npgtech_map = { + .map = { + .scan = npgtech, + .size = ARRAY_SIZE(npgtech), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_NPGTECH, + } +}; + +static int __init init_rc_map_npgtech(void) +{ + return ir_register_map(&npgtech_map); +} + +static void __exit exit_rc_map_npgtech(void) +{ + ir_unregister_map(&npgtech_map); +} + +module_init(init_rc_map_npgtech) +module_exit(exit_rc_map_npgtech) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-pctv-sedna.c b/drivers/media/IR/keymaps/rc-pctv-sedna.c new file mode 100644 index 000000000000..4129bb44a25b --- /dev/null +++ b/drivers/media/IR/keymaps/rc-pctv-sedna.c @@ -0,0 +1,80 @@ +/* pctv-sedna.h - Keytable for pctv_sedna Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Mapping for the 28 key remote control as seen at + http://www.sednacomputer.com/photo/cardbus-tv.jpg + Pavel Mihaylov <bin@bash.info> + Also for the remote bundled with Kozumi KTV-01C card */ + +static struct ir_scancode pctv_sedna[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0a, KEY_AGAIN }, /* Recall */ + { 0x0b, KEY_CHANNELUP }, + { 0x0c, KEY_VOLUMEUP }, + { 0x0d, KEY_MODE }, /* Stereo */ + { 0x0e, KEY_STOP }, + { 0x0f, KEY_PREVIOUSSONG }, + { 0x10, KEY_ZOOM }, + { 0x11, KEY_TUNER }, /* Source */ + { 0x12, KEY_POWER }, + { 0x13, KEY_MUTE }, + { 0x15, KEY_CHANNELDOWN }, + { 0x18, KEY_VOLUMEDOWN }, + { 0x19, KEY_CAMERA }, /* Snapshot */ + { 0x1a, KEY_NEXTSONG }, + { 0x1b, KEY_TIME }, /* Time Shift */ + { 0x1c, KEY_RADIO }, /* FM Radio */ + { 0x1d, KEY_RECORD }, + { 0x1e, KEY_PAUSE }, + /* additional codes for Kozumi's remote */ + { 0x14, KEY_INFO }, /* OSD */ + { 0x16, KEY_OK }, /* OK */ + { 0x17, KEY_DIGITS }, /* Plus */ + { 0x1f, KEY_PLAY }, /* Play */ +}; + +static struct rc_keymap pctv_sedna_map = { + .map = { + .scan = pctv_sedna, + .size = ARRAY_SIZE(pctv_sedna), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PCTV_SEDNA, + } +}; + +static int __init init_rc_map_pctv_sedna(void) +{ + return ir_register_map(&pctv_sedna_map); +} + +static void __exit exit_rc_map_pctv_sedna(void) +{ + ir_unregister_map(&pctv_sedna_map); +} + +module_init(init_rc_map_pctv_sedna) +module_exit(exit_rc_map_pctv_sedna) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-pinnacle-color.c b/drivers/media/IR/keymaps/rc-pinnacle-color.c new file mode 100644 index 000000000000..326e023ce126 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-pinnacle-color.c @@ -0,0 +1,94 @@ +/* pinnacle-color.h - Keytable for pinnacle_color Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode pinnacle_color[] = { + { 0x59, KEY_MUTE }, + { 0x4a, KEY_POWER }, + + { 0x18, KEY_TEXT }, + { 0x26, KEY_TV }, + { 0x3d, KEY_PRINT }, + + { 0x48, KEY_RED }, + { 0x04, KEY_GREEN }, + { 0x11, KEY_YELLOW }, + { 0x00, KEY_BLUE }, + + { 0x2d, KEY_VOLUMEUP }, + { 0x1e, KEY_VOLUMEDOWN }, + + { 0x49, KEY_MENU }, + + { 0x16, KEY_CHANNELUP }, + { 0x17, KEY_CHANNELDOWN }, + + { 0x20, KEY_UP }, + { 0x21, KEY_DOWN }, + { 0x22, KEY_LEFT }, + { 0x23, KEY_RIGHT }, + { 0x0d, KEY_SELECT }, + + { 0x08, KEY_BACK }, + { 0x07, KEY_REFRESH }, + + { 0x2f, KEY_ZOOM }, + { 0x29, KEY_RECORD }, + + { 0x4b, KEY_PAUSE }, + { 0x4d, KEY_REWIND }, + { 0x2e, KEY_PLAY }, + { 0x4e, KEY_FORWARD }, + { 0x53, KEY_PREVIOUS }, + { 0x4c, KEY_STOP }, + { 0x54, KEY_NEXT }, + + { 0x69, KEY_0 }, + { 0x6a, KEY_1 }, + { 0x6b, KEY_2 }, + { 0x6c, KEY_3 }, + { 0x6d, KEY_4 }, + { 0x6e, KEY_5 }, + { 0x6f, KEY_6 }, + { 0x70, KEY_7 }, + { 0x71, KEY_8 }, + { 0x72, KEY_9 }, + + { 0x74, KEY_CHANNEL }, + { 0x0a, KEY_BACKSPACE }, +}; + +static struct rc_keymap pinnacle_color_map = { + .map = { + .scan = pinnacle_color, + .size = ARRAY_SIZE(pinnacle_color), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PINNACLE_COLOR, + } +}; + +static int __init init_rc_map_pinnacle_color(void) +{ + return ir_register_map(&pinnacle_color_map); +} + +static void __exit exit_rc_map_pinnacle_color(void) +{ + ir_unregister_map(&pinnacle_color_map); +} + +module_init(init_rc_map_pinnacle_color) +module_exit(exit_rc_map_pinnacle_color) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-pinnacle-grey.c b/drivers/media/IR/keymaps/rc-pinnacle-grey.c new file mode 100644 index 000000000000..14cb772515c6 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-pinnacle-grey.c @@ -0,0 +1,89 @@ +/* pinnacle-grey.h - Keytable for pinnacle_grey Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode pinnacle_grey[] = { + { 0x3a, KEY_0 }, + { 0x31, KEY_1 }, + { 0x32, KEY_2 }, + { 0x33, KEY_3 }, + { 0x34, KEY_4 }, + { 0x35, KEY_5 }, + { 0x36, KEY_6 }, + { 0x37, KEY_7 }, + { 0x38, KEY_8 }, + { 0x39, KEY_9 }, + + { 0x2f, KEY_POWER }, + + { 0x2e, KEY_P }, + { 0x1f, KEY_L }, + { 0x2b, KEY_I }, + + { 0x2d, KEY_SCREEN }, + { 0x1e, KEY_ZOOM }, + { 0x1b, KEY_VOLUMEUP }, + { 0x0f, KEY_VOLUMEDOWN }, + { 0x17, KEY_CHANNELUP }, + { 0x1c, KEY_CHANNELDOWN }, + { 0x25, KEY_INFO }, + + { 0x3c, KEY_MUTE }, + + { 0x3d, KEY_LEFT }, + { 0x3b, KEY_RIGHT }, + + { 0x3f, KEY_UP }, + { 0x3e, KEY_DOWN }, + { 0x1a, KEY_ENTER }, + + { 0x1d, KEY_MENU }, + { 0x19, KEY_AGAIN }, + { 0x16, KEY_PREVIOUSSONG }, + { 0x13, KEY_NEXTSONG }, + { 0x15, KEY_PAUSE }, + { 0x0e, KEY_REWIND }, + { 0x0d, KEY_PLAY }, + { 0x0b, KEY_STOP }, + { 0x07, KEY_FORWARD }, + { 0x27, KEY_RECORD }, + { 0x26, KEY_TUNER }, + { 0x29, KEY_TEXT }, + { 0x2a, KEY_MEDIA }, + { 0x18, KEY_EPG }, +}; + +static struct rc_keymap pinnacle_grey_map = { + .map = { + .scan = pinnacle_grey, + .size = ARRAY_SIZE(pinnacle_grey), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PINNACLE_GREY, + } +}; + +static int __init init_rc_map_pinnacle_grey(void) +{ + return ir_register_map(&pinnacle_grey_map); +} + +static void __exit exit_rc_map_pinnacle_grey(void) +{ + ir_unregister_map(&pinnacle_grey_map); +} + +module_init(init_rc_map_pinnacle_grey) +module_exit(exit_rc_map_pinnacle_grey) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-pinnacle-pctv-hd.c b/drivers/media/IR/keymaps/rc-pinnacle-pctv-hd.c new file mode 100644 index 000000000000..835bf4ef8de7 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-pinnacle-pctv-hd.c @@ -0,0 +1,73 @@ +/* pinnacle-pctv-hd.h - Keytable for pinnacle_pctv_hd Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Pinnacle PCTV HD 800i mini remote */ + +static struct ir_scancode pinnacle_pctv_hd[] = { + + { 0x0f, KEY_1 }, + { 0x15, KEY_2 }, + { 0x10, KEY_3 }, + { 0x18, KEY_4 }, + { 0x1b, KEY_5 }, + { 0x1e, KEY_6 }, + { 0x11, KEY_7 }, + { 0x21, KEY_8 }, + { 0x12, KEY_9 }, + { 0x27, KEY_0 }, + + { 0x24, KEY_ZOOM }, + { 0x2a, KEY_SUBTITLE }, + + { 0x00, KEY_MUTE }, + { 0x01, KEY_ENTER }, /* Pinnacle Logo */ + { 0x39, KEY_POWER }, + + { 0x03, KEY_VOLUMEUP }, + { 0x09, KEY_VOLUMEDOWN }, + { 0x06, KEY_CHANNELUP }, + { 0x0c, KEY_CHANNELDOWN }, + + { 0x2d, KEY_REWIND }, + { 0x30, KEY_PLAYPAUSE }, + { 0x33, KEY_FASTFORWARD }, + { 0x3c, KEY_STOP }, + { 0x36, KEY_RECORD }, + { 0x3f, KEY_EPG }, /* Labeled "?" */ +}; + +static struct rc_keymap pinnacle_pctv_hd_map = { + .map = { + .scan = pinnacle_pctv_hd, + .size = ARRAY_SIZE(pinnacle_pctv_hd), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PINNACLE_PCTV_HD, + } +}; + +static int __init init_rc_map_pinnacle_pctv_hd(void) +{ + return ir_register_map(&pinnacle_pctv_hd_map); +} + +static void __exit exit_rc_map_pinnacle_pctv_hd(void) +{ + ir_unregister_map(&pinnacle_pctv_hd_map); +} + +module_init(init_rc_map_pinnacle_pctv_hd) +module_exit(exit_rc_map_pinnacle_pctv_hd) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-pixelview-mk12.c b/drivers/media/IR/keymaps/rc-pixelview-mk12.c new file mode 100644 index 000000000000..5a735d569a8b --- /dev/null +++ b/drivers/media/IR/keymaps/rc-pixelview-mk12.c @@ -0,0 +1,83 @@ +/* rc-pixelview-mk12.h - Keytable for pixelview Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + * Keytable for MK-F12 IR remote provided together with Pixelview + * Ultra Pro Remote Controller. Uses NEC extended format. + */ +static struct ir_scancode pixelview_mk12[] = { + { 0x866b03, KEY_TUNER }, /* Timeshift */ + { 0x866b1e, KEY_POWER2 }, /* power */ + + { 0x866b01, KEY_1 }, + { 0x866b0b, KEY_2 }, + { 0x866b1b, KEY_3 }, + { 0x866b05, KEY_4 }, + { 0x866b09, KEY_5 }, + { 0x866b15, KEY_6 }, + { 0x866b06, KEY_7 }, + { 0x866b0a, KEY_8 }, + { 0x866b12, KEY_9 }, + { 0x866b02, KEY_0 }, + + { 0x866b13, KEY_AGAIN }, /* loop */ + { 0x866b10, KEY_DIGITS }, /* +100 */ + + { 0x866b00, KEY_MEDIA }, /* source */ + { 0x866b18, KEY_MUTE }, /* mute */ + { 0x866b19, KEY_CAMERA }, /* snapshot */ + { 0x866b1a, KEY_SEARCH }, /* scan */ + + { 0x866b16, KEY_CHANNELUP }, /* chn + */ + { 0x866b14, KEY_CHANNELDOWN }, /* chn - */ + { 0x866b1f, KEY_VOLUMEUP }, /* vol + */ + { 0x866b17, KEY_VOLUMEDOWN }, /* vol - */ + { 0x866b1c, KEY_ZOOM }, /* zoom */ + + { 0x866b04, KEY_REWIND }, + { 0x866b0e, KEY_RECORD }, + { 0x866b0c, KEY_FORWARD }, + + { 0x866b1d, KEY_STOP }, + { 0x866b08, KEY_PLAY }, + { 0x866b0f, KEY_PAUSE }, + + { 0x866b0d, KEY_TV }, + { 0x866b07, KEY_RADIO }, /* FM */ +}; + +static struct rc_keymap pixelview_map = { + .map = { + .scan = pixelview_mk12, + .size = ARRAY_SIZE(pixelview_mk12), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_PIXELVIEW_MK12, + } +}; + +static int __init init_rc_map_pixelview(void) +{ + return ir_register_map(&pixelview_map); +} + +static void __exit exit_rc_map_pixelview(void) +{ + ir_unregister_map(&pixelview_map); +} + +module_init(init_rc_map_pixelview) +module_exit(exit_rc_map_pixelview) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-pixelview-new.c b/drivers/media/IR/keymaps/rc-pixelview-new.c new file mode 100644 index 000000000000..7bbbbf5735e6 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-pixelview-new.c @@ -0,0 +1,83 @@ +/* pixelview-new.h - Keytable for pixelview_new Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + Mauro Carvalho Chehab <mchehab@infradead.org> + present on PV MPEG 8000GT + */ + +static struct ir_scancode pixelview_new[] = { + { 0x3c, KEY_TIME }, /* Timeshift */ + { 0x12, KEY_POWER }, + + { 0x3d, KEY_1 }, + { 0x38, KEY_2 }, + { 0x18, KEY_3 }, + { 0x35, KEY_4 }, + { 0x39, KEY_5 }, + { 0x15, KEY_6 }, + { 0x36, KEY_7 }, + { 0x3a, KEY_8 }, + { 0x1e, KEY_9 }, + { 0x3e, KEY_0 }, + + { 0x1c, KEY_AGAIN }, /* LOOP */ + { 0x3f, KEY_MEDIA }, /* Source */ + { 0x1f, KEY_LAST }, /* +100 */ + { 0x1b, KEY_MUTE }, + + { 0x17, KEY_CHANNELDOWN }, + { 0x16, KEY_CHANNELUP }, + { 0x10, KEY_VOLUMEUP }, + { 0x14, KEY_VOLUMEDOWN }, + { 0x13, KEY_ZOOM }, + + { 0x19, KEY_CAMERA }, /* SNAPSHOT */ + { 0x1a, KEY_SEARCH }, /* scan */ + + { 0x37, KEY_REWIND }, /* << */ + { 0x32, KEY_RECORD }, /* o (red) */ + { 0x33, KEY_FORWARD }, /* >> */ + { 0x11, KEY_STOP }, /* square */ + { 0x3b, KEY_PLAY }, /* > */ + { 0x30, KEY_PLAYPAUSE }, /* || */ + + { 0x31, KEY_TV }, + { 0x34, KEY_RADIO }, +}; + +static struct rc_keymap pixelview_new_map = { + .map = { + .scan = pixelview_new, + .size = ARRAY_SIZE(pixelview_new), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PIXELVIEW_NEW, + } +}; + +static int __init init_rc_map_pixelview_new(void) +{ + return ir_register_map(&pixelview_new_map); +} + +static void __exit exit_rc_map_pixelview_new(void) +{ + ir_unregister_map(&pixelview_new_map); +} + +module_init(init_rc_map_pixelview_new) +module_exit(exit_rc_map_pixelview_new) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-pixelview.c b/drivers/media/IR/keymaps/rc-pixelview.c new file mode 100644 index 000000000000..82ff12e182a0 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-pixelview.c @@ -0,0 +1,82 @@ +/* pixelview.h - Keytable for pixelview Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode pixelview[] = { + + { 0x1e, KEY_POWER }, /* power */ + { 0x07, KEY_MEDIA }, /* source */ + { 0x1c, KEY_SEARCH }, /* scan */ + + + { 0x03, KEY_TUNER }, /* TV/FM */ + + { 0x00, KEY_RECORD }, + { 0x08, KEY_STOP }, + { 0x11, KEY_PLAY }, + + { 0x1a, KEY_PLAYPAUSE }, /* freeze */ + { 0x19, KEY_ZOOM }, /* zoom */ + { 0x0f, KEY_TEXT }, /* min */ + + { 0x01, KEY_1 }, + { 0x0b, KEY_2 }, + { 0x1b, KEY_3 }, + { 0x05, KEY_4 }, + { 0x09, KEY_5 }, + { 0x15, KEY_6 }, + { 0x06, KEY_7 }, + { 0x0a, KEY_8 }, + { 0x12, KEY_9 }, + { 0x02, KEY_0 }, + { 0x10, KEY_LAST }, /* +100 */ + { 0x13, KEY_LIST }, /* recall */ + + { 0x1f, KEY_CHANNELUP }, /* chn down */ + { 0x17, KEY_CHANNELDOWN }, /* chn up */ + { 0x16, KEY_VOLUMEUP }, /* vol down */ + { 0x14, KEY_VOLUMEDOWN }, /* vol up */ + + { 0x04, KEY_KPMINUS }, /* <<< */ + { 0x0e, KEY_SETUP }, /* function */ + { 0x0c, KEY_KPPLUS }, /* >>> */ + + { 0x0d, KEY_GOTO }, /* mts */ + { 0x1d, KEY_REFRESH }, /* reset */ + { 0x18, KEY_MUTE }, /* mute/unmute */ +}; + +static struct rc_keymap pixelview_map = { + .map = { + .scan = pixelview, + .size = ARRAY_SIZE(pixelview), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PIXELVIEW, + } +}; + +static int __init init_rc_map_pixelview(void) +{ + return ir_register_map(&pixelview_map); +} + +static void __exit exit_rc_map_pixelview(void) +{ + ir_unregister_map(&pixelview_map); +} + +module_init(init_rc_map_pixelview) +module_exit(exit_rc_map_pixelview) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-powercolor-real-angel.c b/drivers/media/IR/keymaps/rc-powercolor-real-angel.c new file mode 100644 index 000000000000..7cef8190a224 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-powercolor-real-angel.c @@ -0,0 +1,81 @@ +/* powercolor-real-angel.h - Keytable for powercolor_real_angel Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + * Remote control for Powercolor Real Angel 330 + * Daniel Fraga <fragabr@gmail.com> + */ + +static struct ir_scancode powercolor_real_angel[] = { + { 0x38, KEY_SWITCHVIDEOMODE }, /* switch inputs */ + { 0x0c, KEY_MEDIA }, /* Turn ON/OFF App */ + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0a, KEY_DIGITS }, /* single, double, tripple digit */ + { 0x29, KEY_PREVIOUS }, /* previous channel */ + { 0x12, KEY_BRIGHTNESSUP }, + { 0x13, KEY_BRIGHTNESSDOWN }, + { 0x2b, KEY_MODE }, /* stereo/mono */ + { 0x2c, KEY_TEXT }, /* teletext */ + { 0x20, KEY_CHANNELUP }, /* channel up */ + { 0x21, KEY_CHANNELDOWN }, /* channel down */ + { 0x10, KEY_VOLUMEUP }, /* volume up */ + { 0x11, KEY_VOLUMEDOWN }, /* volume down */ + { 0x0d, KEY_MUTE }, + { 0x1f, KEY_RECORD }, + { 0x17, KEY_PLAY }, + { 0x16, KEY_PAUSE }, + { 0x0b, KEY_STOP }, + { 0x27, KEY_FASTFORWARD }, + { 0x26, KEY_REWIND }, + { 0x1e, KEY_SEARCH }, /* autoscan */ + { 0x0e, KEY_CAMERA }, /* snapshot */ + { 0x2d, KEY_SETUP }, + { 0x0f, KEY_SCREEN }, /* full screen */ + { 0x14, KEY_RADIO }, /* FM radio */ + { 0x25, KEY_POWER }, /* power */ +}; + +static struct rc_keymap powercolor_real_angel_map = { + .map = { + .scan = powercolor_real_angel, + .size = ARRAY_SIZE(powercolor_real_angel), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_POWERCOLOR_REAL_ANGEL, + } +}; + +static int __init init_rc_map_powercolor_real_angel(void) +{ + return ir_register_map(&powercolor_real_angel_map); +} + +static void __exit exit_rc_map_powercolor_real_angel(void) +{ + ir_unregister_map(&powercolor_real_angel_map); +} + +module_init(init_rc_map_powercolor_real_angel) +module_exit(exit_rc_map_powercolor_real_angel) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-proteus-2309.c b/drivers/media/IR/keymaps/rc-proteus-2309.c new file mode 100644 index 000000000000..22e92d39dee5 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-proteus-2309.c @@ -0,0 +1,69 @@ +/* proteus-2309.h - Keytable for proteus_2309 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Michal Majchrowicz <mmajchrowicz@gmail.com> */ + +static struct ir_scancode proteus_2309[] = { + /* numeric */ + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x5c, KEY_POWER }, /* power */ + { 0x20, KEY_ZOOM }, /* full screen */ + { 0x0f, KEY_BACKSPACE }, /* recall */ + { 0x1b, KEY_ENTER }, /* mute */ + { 0x41, KEY_RECORD }, /* record */ + { 0x43, KEY_STOP }, /* stop */ + { 0x16, KEY_S }, + { 0x1a, KEY_POWER2 }, /* off */ + { 0x2e, KEY_RED }, + { 0x1f, KEY_CHANNELDOWN }, /* channel - */ + { 0x1c, KEY_CHANNELUP }, /* channel + */ + { 0x10, KEY_VOLUMEDOWN }, /* volume - */ + { 0x1e, KEY_VOLUMEUP }, /* volume + */ + { 0x14, KEY_F1 }, +}; + +static struct rc_keymap proteus_2309_map = { + .map = { + .scan = proteus_2309, + .size = ARRAY_SIZE(proteus_2309), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PROTEUS_2309, + } +}; + +static int __init init_rc_map_proteus_2309(void) +{ + return ir_register_map(&proteus_2309_map); +} + +static void __exit exit_rc_map_proteus_2309(void) +{ + ir_unregister_map(&proteus_2309_map); +} + +module_init(init_rc_map_proteus_2309) +module_exit(exit_rc_map_proteus_2309) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-purpletv.c b/drivers/media/IR/keymaps/rc-purpletv.c new file mode 100644 index 000000000000..4e20fc2269f7 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-purpletv.c @@ -0,0 +1,81 @@ +/* purpletv.h - Keytable for purpletv Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode purpletv[] = { + { 0x03, KEY_POWER }, + { 0x6f, KEY_MUTE }, + { 0x10, KEY_BACKSPACE }, /* Recall */ + + { 0x11, KEY_0 }, + { 0x04, KEY_1 }, + { 0x05, KEY_2 }, + { 0x06, KEY_3 }, + { 0x08, KEY_4 }, + { 0x09, KEY_5 }, + { 0x0a, KEY_6 }, + { 0x0c, KEY_7 }, + { 0x0d, KEY_8 }, + { 0x0e, KEY_9 }, + { 0x12, KEY_DOT }, /* 100+ */ + + { 0x07, KEY_VOLUMEUP }, + { 0x0b, KEY_VOLUMEDOWN }, + { 0x1a, KEY_KPPLUS }, + { 0x18, KEY_KPMINUS }, + { 0x15, KEY_UP }, + { 0x1d, KEY_DOWN }, + { 0x0f, KEY_CHANNELUP }, + { 0x13, KEY_CHANNELDOWN }, + { 0x48, KEY_ZOOM }, + + { 0x1b, KEY_VIDEO }, /* Video source */ + { 0x1f, KEY_CAMERA }, /* Snapshot */ + { 0x49, KEY_LANGUAGE }, /* MTS Select */ + { 0x19, KEY_SEARCH }, /* Auto Scan */ + + { 0x4b, KEY_RECORD }, + { 0x46, KEY_PLAY }, + { 0x45, KEY_PAUSE }, /* Pause */ + { 0x44, KEY_STOP }, + { 0x43, KEY_TIME }, /* Time Shift */ + { 0x17, KEY_CHANNEL }, /* SURF CH */ + { 0x40, KEY_FORWARD }, /* Forward ? */ + { 0x42, KEY_REWIND }, /* Backward ? */ + +}; + +static struct rc_keymap purpletv_map = { + .map = { + .scan = purpletv, + .size = ARRAY_SIZE(purpletv), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PURPLETV, + } +}; + +static int __init init_rc_map_purpletv(void) +{ + return ir_register_map(&purpletv_map); +} + +static void __exit exit_rc_map_purpletv(void) +{ + ir_unregister_map(&purpletv_map); +} + +module_init(init_rc_map_purpletv) +module_exit(exit_rc_map_purpletv) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-pv951.c b/drivers/media/IR/keymaps/rc-pv951.c new file mode 100644 index 000000000000..36679e706cf3 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-pv951.c @@ -0,0 +1,78 @@ +/* pv951.h - Keytable for pv951 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Mark Phalan <phalanm@o2.ie> */ + +static struct ir_scancode pv951[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x12, KEY_POWER }, + { 0x10, KEY_MUTE }, + { 0x1f, KEY_VOLUMEDOWN }, + { 0x1b, KEY_VOLUMEUP }, + { 0x1a, KEY_CHANNELUP }, + { 0x1e, KEY_CHANNELDOWN }, + { 0x0e, KEY_PAGEUP }, + { 0x1d, KEY_PAGEDOWN }, + { 0x13, KEY_SOUND }, + + { 0x18, KEY_KPPLUSMINUS }, /* CH +/- */ + { 0x16, KEY_SUBTITLE }, /* CC */ + { 0x0d, KEY_TEXT }, /* TTX */ + { 0x0b, KEY_TV }, /* AIR/CBL */ + { 0x11, KEY_PC }, /* PC/TV */ + { 0x17, KEY_OK }, /* CH RTN */ + { 0x19, KEY_MODE }, /* FUNC */ + { 0x0c, KEY_SEARCH }, /* AUTOSCAN */ + + /* Not sure what to do with these ones! */ + { 0x0f, KEY_SELECT }, /* SOURCE */ + { 0x0a, KEY_KPPLUS }, /* +100 */ + { 0x14, KEY_EQUAL }, /* SYNC */ + { 0x1c, KEY_MEDIA }, /* PC/TV */ +}; + +static struct rc_keymap pv951_map = { + .map = { + .scan = pv951, + .size = ARRAY_SIZE(pv951), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_PV951, + } +}; + +static int __init init_rc_map_pv951(void) +{ + return ir_register_map(&pv951_map); +} + +static void __exit exit_rc_map_pv951(void) +{ + ir_unregister_map(&pv951_map); +} + +module_init(init_rc_map_pv951) +module_exit(exit_rc_map_pv951) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-rc5-hauppauge-new.c b/drivers/media/IR/keymaps/rc-rc5-hauppauge-new.c new file mode 100644 index 000000000000..cc6b8f548747 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-rc5-hauppauge-new.c @@ -0,0 +1,103 @@ +/* rc5-hauppauge-new.h - Keytable for rc5_hauppauge_new Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* + * Hauppauge:the newer, gray remotes (seems there are multiple + * slightly different versions), shipped with cx88+ivtv cards. + * + * This table contains the complete RC5 code, instead of just the data part + */ + +static struct ir_scancode rc5_hauppauge_new[] = { + /* Keys 0 to 9 */ + { 0x1e00, KEY_0 }, + { 0x1e01, KEY_1 }, + { 0x1e02, KEY_2 }, + { 0x1e03, KEY_3 }, + { 0x1e04, KEY_4 }, + { 0x1e05, KEY_5 }, + { 0x1e06, KEY_6 }, + { 0x1e07, KEY_7 }, + { 0x1e08, KEY_8 }, + { 0x1e09, KEY_9 }, + + { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */ + { 0x1e0b, KEY_RED }, /* red button */ + { 0x1e0c, KEY_RADIO }, + { 0x1e0d, KEY_MENU }, + { 0x1e0e, KEY_SUBTITLE }, /* also the # key */ + { 0x1e0f, KEY_MUTE }, + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + { 0x1e12, KEY_PREVIOUS }, /* previous channel */ + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e18, KEY_VIDEO }, /* Videos */ + { 0x1e19, KEY_AUDIO }, /* Music */ + /* 0x1e1a: Pictures - presume this means + "Multimedia Home Platform" - + no "PICTURES" key in input.h + */ + { 0x1e1a, KEY_MHP }, + + { 0x1e1b, KEY_EPG }, /* Guide */ + { 0x1e1c, KEY_TV }, + { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ + { 0x1e1f, KEY_EXIT }, /* back/exit */ + { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ + { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */ + { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x1e25, KEY_ENTER }, /* OK */ + { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */ + { 0x1e29, KEY_BLUE }, /* blue key */ + { 0x1e2e, KEY_GREEN }, /* green button */ + { 0x1e30, KEY_PAUSE }, /* pause */ + { 0x1e32, KEY_REWIND }, /* backward << */ + { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ + { 0x1e35, KEY_PLAY }, + { 0x1e36, KEY_STOP }, + { 0x1e37, KEY_RECORD }, /* recording */ + { 0x1e38, KEY_YELLOW }, /* yellow key */ + { 0x1e3b, KEY_SELECT }, /* top right button */ + { 0x1e3c, KEY_ZOOM }, /* full */ + { 0x1e3d, KEY_POWER }, /* system power (green button) */ +}; + +static struct rc_keymap rc5_hauppauge_new_map = { + .map = { + .scan = rc5_hauppauge_new, + .size = ARRAY_SIZE(rc5_hauppauge_new), + .ir_type = IR_TYPE_RC5, + .name = RC_MAP_RC5_HAUPPAUGE_NEW, + } +}; + +static int __init init_rc_map_rc5_hauppauge_new(void) +{ + return ir_register_map(&rc5_hauppauge_new_map); +} + +static void __exit exit_rc_map_rc5_hauppauge_new(void) +{ + ir_unregister_map(&rc5_hauppauge_new_map); +} + +module_init(init_rc_map_rc5_hauppauge_new) +module_exit(exit_rc_map_rc5_hauppauge_new) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-rc5-tv.c b/drivers/media/IR/keymaps/rc-rc5-tv.c new file mode 100644 index 000000000000..73cce2f8ddfb --- /dev/null +++ b/drivers/media/IR/keymaps/rc-rc5-tv.c @@ -0,0 +1,81 @@ +/* rc5-tv.h - Keytable for rc5_tv Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* generic RC5 keytable */ +/* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */ +/* used by old (black) Hauppauge remotes */ + +static struct ir_scancode rc5_tv[] = { + /* Keys 0 to 9 */ + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + + { 0x0b, KEY_CHANNEL }, /* channel / program (japan: 11) */ + { 0x0c, KEY_POWER }, /* standby */ + { 0x0d, KEY_MUTE }, /* mute / demute */ + { 0x0f, KEY_TV }, /* display */ + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x12, KEY_BRIGHTNESSUP }, + { 0x13, KEY_BRIGHTNESSDOWN }, + { 0x1e, KEY_SEARCH }, /* search + */ + { 0x20, KEY_CHANNELUP }, /* channel / program + */ + { 0x21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x22, KEY_CHANNEL }, /* alt / channel */ + { 0x23, KEY_LANGUAGE }, /* 1st / 2nd language */ + { 0x26, KEY_SLEEP }, /* sleeptimer */ + { 0x2e, KEY_MENU }, /* 2nd controls (USA: menu) */ + { 0x30, KEY_PAUSE }, + { 0x32, KEY_REWIND }, + { 0x33, KEY_GOTO }, + { 0x35, KEY_PLAY }, + { 0x36, KEY_STOP }, + { 0x37, KEY_RECORD }, /* recording */ + { 0x3c, KEY_TEXT }, /* teletext submode (Japan: 12) */ + { 0x3d, KEY_SUSPEND }, /* system standby */ + +}; + +static struct rc_keymap rc5_tv_map = { + .map = { + .scan = rc5_tv, + .size = ARRAY_SIZE(rc5_tv), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_RC5_TV, + } +}; + +static int __init init_rc_map_rc5_tv(void) +{ + return ir_register_map(&rc5_tv_map); +} + +static void __exit exit_rc_map_rc5_tv(void) +{ + ir_unregister_map(&rc5_tv_map); +} + +module_init(init_rc_map_rc5_tv) +module_exit(exit_rc_map_rc5_tv) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-real-audio-220-32-keys.c b/drivers/media/IR/keymaps/rc-real-audio-220-32-keys.c new file mode 100644 index 000000000000..ab1a6d2baf72 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-real-audio-220-32-keys.c @@ -0,0 +1,78 @@ +/* real-audio-220-32-keys.h - Keytable for real_audio_220_32_keys Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Zogis Real Audio 220 - 32 keys IR */ + +static struct ir_scancode real_audio_220_32_keys[] = { + { 0x1c, KEY_RADIO}, + { 0x12, KEY_POWER2}, + + { 0x01, KEY_1}, + { 0x02, KEY_2}, + { 0x03, KEY_3}, + { 0x04, KEY_4}, + { 0x05, KEY_5}, + { 0x06, KEY_6}, + { 0x07, KEY_7}, + { 0x08, KEY_8}, + { 0x09, KEY_9}, + { 0x00, KEY_0}, + + { 0x0c, KEY_VOLUMEUP}, + { 0x18, KEY_VOLUMEDOWN}, + { 0x0b, KEY_CHANNELUP}, + { 0x15, KEY_CHANNELDOWN}, + { 0x16, KEY_ENTER}, + + { 0x11, KEY_LIST}, /* Source */ + { 0x0d, KEY_AUDIO}, /* stereo */ + + { 0x0f, KEY_PREVIOUS}, /* Prev */ + { 0x1b, KEY_TIME}, /* Timeshift */ + { 0x1a, KEY_NEXT}, /* Next */ + + { 0x0e, KEY_STOP}, + { 0x1f, KEY_PLAY}, + { 0x1e, KEY_PLAYPAUSE}, /* Pause */ + + { 0x1d, KEY_RECORD}, + { 0x13, KEY_MUTE}, + { 0x19, KEY_CAMERA}, /* Snapshot */ + +}; + +static struct rc_keymap real_audio_220_32_keys_map = { + .map = { + .scan = real_audio_220_32_keys, + .size = ARRAY_SIZE(real_audio_220_32_keys), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_REAL_AUDIO_220_32_KEYS, + } +}; + +static int __init init_rc_map_real_audio_220_32_keys(void) +{ + return ir_register_map(&real_audio_220_32_keys_map); +} + +static void __exit exit_rc_map_real_audio_220_32_keys(void) +{ + ir_unregister_map(&real_audio_220_32_keys_map); +} + +module_init(init_rc_map_real_audio_220_32_keys) +module_exit(exit_rc_map_real_audio_220_32_keys) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-tbs-nec.c b/drivers/media/IR/keymaps/rc-tbs-nec.c new file mode 100644 index 000000000000..3309631e6f80 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-tbs-nec.c @@ -0,0 +1,73 @@ +/* tbs-nec.h - Keytable for tbs_nec Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode tbs_nec[] = { + { 0x04, KEY_POWER2}, /*power*/ + { 0x14, KEY_MUTE}, /*mute*/ + { 0x07, KEY_1}, + { 0x06, KEY_2}, + { 0x05, KEY_3}, + { 0x0b, KEY_4}, + { 0x0a, KEY_5}, + { 0x09, KEY_6}, + { 0x0f, KEY_7}, + { 0x0e, KEY_8}, + { 0x0d, KEY_9}, + { 0x12, KEY_0}, + { 0x16, KEY_CHANNELUP}, /*ch+*/ + { 0x11, KEY_CHANNELDOWN},/*ch-*/ + { 0x13, KEY_VOLUMEUP}, /*vol+*/ + { 0x0c, KEY_VOLUMEDOWN},/*vol-*/ + { 0x03, KEY_RECORD}, /*rec*/ + { 0x18, KEY_PAUSE}, /*pause*/ + { 0x19, KEY_OK}, /*ok*/ + { 0x1a, KEY_CAMERA}, /* snapshot */ + { 0x01, KEY_UP}, + { 0x10, KEY_LEFT}, + { 0x02, KEY_RIGHT}, + { 0x08, KEY_DOWN}, + { 0x15, KEY_FAVORITES}, + { 0x17, KEY_SUBTITLE}, + { 0x1d, KEY_ZOOM}, + { 0x1f, KEY_EXIT}, + { 0x1e, KEY_MENU}, + { 0x1c, KEY_EPG}, + { 0x00, KEY_PREVIOUS}, + { 0x1b, KEY_MODE}, +}; + +static struct rc_keymap tbs_nec_map = { + .map = { + .scan = tbs_nec, + .size = ARRAY_SIZE(tbs_nec), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_TBS_NEC, + } +}; + +static int __init init_rc_map_tbs_nec(void) +{ + return ir_register_map(&tbs_nec_map); +} + +static void __exit exit_rc_map_tbs_nec(void) +{ + ir_unregister_map(&tbs_nec_map); +} + +module_init(init_rc_map_tbs_nec) +module_exit(exit_rc_map_tbs_nec) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-terratec-cinergy-xs.c b/drivers/media/IR/keymaps/rc-terratec-cinergy-xs.c new file mode 100644 index 000000000000..5326a0b444c1 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-terratec-cinergy-xs.c @@ -0,0 +1,92 @@ +/* terratec-cinergy-xs.h - Keytable for terratec_cinergy_xs Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Terratec Cinergy Hybrid T USB XS + Devin Heitmueller <dheitmueller@linuxtv.org> + */ + +static struct ir_scancode terratec_cinergy_xs[] = { + { 0x41, KEY_HOME}, + { 0x01, KEY_POWER}, + { 0x42, KEY_MENU}, + { 0x02, KEY_1}, + { 0x03, KEY_2}, + { 0x04, KEY_3}, + { 0x43, KEY_SUBTITLE}, + { 0x05, KEY_4}, + { 0x06, KEY_5}, + { 0x07, KEY_6}, + { 0x44, KEY_TEXT}, + { 0x08, KEY_7}, + { 0x09, KEY_8}, + { 0x0a, KEY_9}, + { 0x45, KEY_DELETE}, + { 0x0b, KEY_TUNER}, + { 0x0c, KEY_0}, + { 0x0d, KEY_MODE}, + { 0x46, KEY_TV}, + { 0x47, KEY_DVD}, + { 0x49, KEY_VIDEO}, + { 0x4b, KEY_AUX}, + { 0x10, KEY_UP}, + { 0x11, KEY_LEFT}, + { 0x12, KEY_OK}, + { 0x13, KEY_RIGHT}, + { 0x14, KEY_DOWN}, + { 0x0f, KEY_EPG}, + { 0x16, KEY_INFO}, + { 0x4d, KEY_BACKSPACE}, + { 0x1c, KEY_VOLUMEUP}, + { 0x4c, KEY_PLAY}, + { 0x1b, KEY_CHANNELUP}, + { 0x1e, KEY_VOLUMEDOWN}, + { 0x1d, KEY_MUTE}, + { 0x1f, KEY_CHANNELDOWN}, + { 0x17, KEY_RED}, + { 0x18, KEY_GREEN}, + { 0x19, KEY_YELLOW}, + { 0x1a, KEY_BLUE}, + { 0x58, KEY_RECORD}, + { 0x48, KEY_STOP}, + { 0x40, KEY_PAUSE}, + { 0x54, KEY_LAST}, + { 0x4e, KEY_REWIND}, + { 0x4f, KEY_FASTFORWARD}, + { 0x5c, KEY_NEXT}, +}; + +static struct rc_keymap terratec_cinergy_xs_map = { + .map = { + .scan = terratec_cinergy_xs, + .size = ARRAY_SIZE(terratec_cinergy_xs), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_TERRATEC_CINERGY_XS, + } +}; + +static int __init init_rc_map_terratec_cinergy_xs(void) +{ + return ir_register_map(&terratec_cinergy_xs_map); +} + +static void __exit exit_rc_map_terratec_cinergy_xs(void) +{ + ir_unregister_map(&terratec_cinergy_xs_map); +} + +module_init(init_rc_map_terratec_cinergy_xs) +module_exit(exit_rc_map_terratec_cinergy_xs) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-tevii-nec.c b/drivers/media/IR/keymaps/rc-tevii-nec.c new file mode 100644 index 000000000000..e30d411c07bb --- /dev/null +++ b/drivers/media/IR/keymaps/rc-tevii-nec.c @@ -0,0 +1,88 @@ +/* tevii-nec.h - Keytable for tevii_nec Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode tevii_nec[] = { + { 0x0a, KEY_POWER2}, + { 0x0c, KEY_MUTE}, + { 0x11, KEY_1}, + { 0x12, KEY_2}, + { 0x13, KEY_3}, + { 0x14, KEY_4}, + { 0x15, KEY_5}, + { 0x16, KEY_6}, + { 0x17, KEY_7}, + { 0x18, KEY_8}, + { 0x19, KEY_9}, + { 0x10, KEY_0}, + { 0x1c, KEY_MENU}, + { 0x0f, KEY_VOLUMEDOWN}, + { 0x1a, KEY_LAST}, + { 0x0e, KEY_OPEN}, + { 0x04, KEY_RECORD}, + { 0x09, KEY_VOLUMEUP}, + { 0x08, KEY_CHANNELUP}, + { 0x07, KEY_PVR}, + { 0x0b, KEY_TIME}, + { 0x02, KEY_RIGHT}, + { 0x03, KEY_LEFT}, + { 0x00, KEY_UP}, + { 0x1f, KEY_OK}, + { 0x01, KEY_DOWN}, + { 0x05, KEY_TUNER}, + { 0x06, KEY_CHANNELDOWN}, + { 0x40, KEY_PLAYPAUSE}, + { 0x1e, KEY_REWIND}, + { 0x1b, KEY_FAVORITES}, + { 0x1d, KEY_BACK}, + { 0x4d, KEY_FASTFORWARD}, + { 0x44, KEY_EPG}, + { 0x4c, KEY_INFO}, + { 0x41, KEY_AB}, + { 0x43, KEY_AUDIO}, + { 0x45, KEY_SUBTITLE}, + { 0x4a, KEY_LIST}, + { 0x46, KEY_F1}, + { 0x47, KEY_F2}, + { 0x5e, KEY_F3}, + { 0x5c, KEY_F4}, + { 0x52, KEY_F5}, + { 0x5a, KEY_F6}, + { 0x56, KEY_MODE}, + { 0x58, KEY_SWITCHVIDEOMODE}, +}; + +static struct rc_keymap tevii_nec_map = { + .map = { + .scan = tevii_nec, + .size = ARRAY_SIZE(tevii_nec), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_TEVII_NEC, + } +}; + +static int __init init_rc_map_tevii_nec(void) +{ + return ir_register_map(&tevii_nec_map); +} + +static void __exit exit_rc_map_tevii_nec(void) +{ + ir_unregister_map(&tevii_nec_map); +} + +module_init(init_rc_map_tevii_nec) +module_exit(exit_rc_map_tevii_nec) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-tt-1500.c b/drivers/media/IR/keymaps/rc-tt-1500.c new file mode 100644 index 000000000000..bc88de011d5d --- /dev/null +++ b/drivers/media/IR/keymaps/rc-tt-1500.c @@ -0,0 +1,82 @@ +/* tt-1500.h - Keytable for tt_1500 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* for the Technotrend 1500 bundled remotes (grey and black): */ + +static struct ir_scancode tt_1500[] = { + { 0x01, KEY_POWER }, + { 0x02, KEY_SHUFFLE }, /* ? double-arrow key */ + { 0x03, KEY_1 }, + { 0x04, KEY_2 }, + { 0x05, KEY_3 }, + { 0x06, KEY_4 }, + { 0x07, KEY_5 }, + { 0x08, KEY_6 }, + { 0x09, KEY_7 }, + { 0x0a, KEY_8 }, + { 0x0b, KEY_9 }, + { 0x0c, KEY_0 }, + { 0x0d, KEY_UP }, + { 0x0e, KEY_LEFT }, + { 0x0f, KEY_OK }, + { 0x10, KEY_RIGHT }, + { 0x11, KEY_DOWN }, + { 0x12, KEY_INFO }, + { 0x13, KEY_EXIT }, + { 0x14, KEY_RED }, + { 0x15, KEY_GREEN }, + { 0x16, KEY_YELLOW }, + { 0x17, KEY_BLUE }, + { 0x18, KEY_MUTE }, + { 0x19, KEY_TEXT }, + { 0x1a, KEY_MODE }, /* ? TV/Radio */ + { 0x21, KEY_OPTION }, + { 0x22, KEY_EPG }, + { 0x23, KEY_CHANNELUP }, + { 0x24, KEY_CHANNELDOWN }, + { 0x25, KEY_VOLUMEUP }, + { 0x26, KEY_VOLUMEDOWN }, + { 0x27, KEY_SETUP }, + { 0x3a, KEY_RECORD }, /* these keys are only in the black remote */ + { 0x3b, KEY_PLAY }, + { 0x3c, KEY_STOP }, + { 0x3d, KEY_REWIND }, + { 0x3e, KEY_PAUSE }, + { 0x3f, KEY_FORWARD }, +}; + +static struct rc_keymap tt_1500_map = { + .map = { + .scan = tt_1500, + .size = ARRAY_SIZE(tt_1500), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_TT_1500, + } +}; + +static int __init init_rc_map_tt_1500(void) +{ + return ir_register_map(&tt_1500_map); +} + +static void __exit exit_rc_map_tt_1500(void) +{ + ir_unregister_map(&tt_1500_map); +} + +module_init(init_rc_map_tt_1500) +module_exit(exit_rc_map_tt_1500) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-videomate-s350.c b/drivers/media/IR/keymaps/rc-videomate-s350.c new file mode 100644 index 000000000000..4df7fcd1d2fc --- /dev/null +++ b/drivers/media/IR/keymaps/rc-videomate-s350.c @@ -0,0 +1,85 @@ +/* videomate-s350.h - Keytable for videomate_s350 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode videomate_s350[] = { + { 0x00, KEY_TV}, + { 0x01, KEY_DVD}, + { 0x04, KEY_RECORD}, + { 0x05, KEY_VIDEO}, /* TV/Video */ + { 0x07, KEY_STOP}, + { 0x08, KEY_PLAYPAUSE}, + { 0x0a, KEY_REWIND}, + { 0x0f, KEY_FASTFORWARD}, + { 0x10, KEY_CHANNELUP}, + { 0x12, KEY_VOLUMEUP}, + { 0x13, KEY_CHANNELDOWN}, + { 0x14, KEY_MUTE}, + { 0x15, KEY_VOLUMEDOWN}, + { 0x16, KEY_1}, + { 0x17, KEY_2}, + { 0x18, KEY_3}, + { 0x19, KEY_4}, + { 0x1a, KEY_5}, + { 0x1b, KEY_6}, + { 0x1c, KEY_7}, + { 0x1d, KEY_8}, + { 0x1e, KEY_9}, + { 0x1f, KEY_0}, + { 0x21, KEY_SLEEP}, + { 0x24, KEY_ZOOM}, + { 0x25, KEY_LAST}, /* Recall */ + { 0x26, KEY_SUBTITLE}, /* CC */ + { 0x27, KEY_LANGUAGE}, /* MTS */ + { 0x29, KEY_CHANNEL}, /* SURF */ + { 0x2b, KEY_A}, + { 0x2c, KEY_B}, + { 0x2f, KEY_CAMERA}, /* Snapshot */ + { 0x23, KEY_RADIO}, + { 0x02, KEY_PREVIOUSSONG}, + { 0x06, KEY_NEXTSONG}, + { 0x03, KEY_EPG}, + { 0x09, KEY_SETUP}, + { 0x22, KEY_BACKSPACE}, + { 0x0c, KEY_UP}, + { 0x0e, KEY_DOWN}, + { 0x0b, KEY_LEFT}, + { 0x0d, KEY_RIGHT}, + { 0x11, KEY_ENTER}, + { 0x20, KEY_TEXT}, +}; + +static struct rc_keymap videomate_s350_map = { + .map = { + .scan = videomate_s350, + .size = ARRAY_SIZE(videomate_s350), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_VIDEOMATE_S350, + } +}; + +static int __init init_rc_map_videomate_s350(void) +{ + return ir_register_map(&videomate_s350_map); +} + +static void __exit exit_rc_map_videomate_s350(void) +{ + ir_unregister_map(&videomate_s350_map); +} + +module_init(init_rc_map_videomate_s350) +module_exit(exit_rc_map_videomate_s350) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-videomate-tv-pvr.c b/drivers/media/IR/keymaps/rc-videomate-tv-pvr.c new file mode 100644 index 000000000000..776b0a638d87 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-videomate-tv-pvr.c @@ -0,0 +1,87 @@ +/* videomate-tv-pvr.h - Keytable for videomate_tv_pvr Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode videomate_tv_pvr[] = { + { 0x14, KEY_MUTE }, + { 0x24, KEY_ZOOM }, + + { 0x01, KEY_DVD }, + { 0x23, KEY_RADIO }, + { 0x00, KEY_TV }, + + { 0x0a, KEY_REWIND }, + { 0x08, KEY_PLAYPAUSE }, + { 0x0f, KEY_FORWARD }, + + { 0x02, KEY_PREVIOUS }, + { 0x07, KEY_STOP }, + { 0x06, KEY_NEXT }, + + { 0x0c, KEY_UP }, + { 0x0e, KEY_DOWN }, + { 0x0b, KEY_LEFT }, + { 0x0d, KEY_RIGHT }, + { 0x11, KEY_OK }, + + { 0x03, KEY_MENU }, + { 0x09, KEY_SETUP }, + { 0x05, KEY_VIDEO }, + { 0x22, KEY_CHANNEL }, + + { 0x12, KEY_VOLUMEUP }, + { 0x15, KEY_VOLUMEDOWN }, + { 0x10, KEY_CHANNELUP }, + { 0x13, KEY_CHANNELDOWN }, + + { 0x04, KEY_RECORD }, + + { 0x16, KEY_1 }, + { 0x17, KEY_2 }, + { 0x18, KEY_3 }, + { 0x19, KEY_4 }, + { 0x1a, KEY_5 }, + { 0x1b, KEY_6 }, + { 0x1c, KEY_7 }, + { 0x1d, KEY_8 }, + { 0x1e, KEY_9 }, + { 0x1f, KEY_0 }, + + { 0x20, KEY_LANGUAGE }, + { 0x21, KEY_SLEEP }, +}; + +static struct rc_keymap videomate_tv_pvr_map = { + .map = { + .scan = videomate_tv_pvr, + .size = ARRAY_SIZE(videomate_tv_pvr), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_VIDEOMATE_TV_PVR, + } +}; + +static int __init init_rc_map_videomate_tv_pvr(void) +{ + return ir_register_map(&videomate_tv_pvr_map); +} + +static void __exit exit_rc_map_videomate_tv_pvr(void) +{ + ir_unregister_map(&videomate_tv_pvr_map); +} + +module_init(init_rc_map_videomate_tv_pvr) +module_exit(exit_rc_map_videomate_tv_pvr) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-winfast-usbii-deluxe.c b/drivers/media/IR/keymaps/rc-winfast-usbii-deluxe.c new file mode 100644 index 000000000000..9d2d550aaa90 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-winfast-usbii-deluxe.c @@ -0,0 +1,82 @@ +/* winfast-usbii-deluxe.h - Keytable for winfast_usbii_deluxe Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Leadtek Winfast TV USB II Deluxe remote + Magnus Alm <magnus.alm@gmail.com> + */ + +static struct ir_scancode winfast_usbii_deluxe[] = { + { 0x62, KEY_0}, + { 0x75, KEY_1}, + { 0x76, KEY_2}, + { 0x77, KEY_3}, + { 0x79, KEY_4}, + { 0x7a, KEY_5}, + { 0x7b, KEY_6}, + { 0x7d, KEY_7}, + { 0x7e, KEY_8}, + { 0x7f, KEY_9}, + + { 0x38, KEY_CAMERA}, /* SNAPSHOT */ + { 0x37, KEY_RECORD}, /* RECORD */ + { 0x35, KEY_TIME}, /* TIMESHIFT */ + + { 0x74, KEY_VOLUMEUP}, /* VOLUMEUP */ + { 0x78, KEY_VOLUMEDOWN}, /* VOLUMEDOWN */ + { 0x64, KEY_MUTE}, /* MUTE */ + + { 0x21, KEY_CHANNEL}, /* SURF */ + { 0x7c, KEY_CHANNELUP}, /* CHANNELUP */ + { 0x60, KEY_CHANNELDOWN}, /* CHANNELDOWN */ + { 0x61, KEY_LAST}, /* LAST CHANNEL (RECALL) */ + + { 0x72, KEY_VIDEO}, /* INPUT MODES (TV/FM) */ + + { 0x70, KEY_POWER2}, /* TV ON/OFF */ + + { 0x39, KEY_CYCLEWINDOWS}, /* MINIMIZE (BOSS) */ + { 0x3a, KEY_NEW}, /* PIP */ + { 0x73, KEY_ZOOM}, /* FULLSECREEN */ + + { 0x66, KEY_INFO}, /* OSD (DISPLAY) */ + + { 0x31, KEY_DOT}, /* '.' */ + { 0x63, KEY_ENTER}, /* ENTER */ + +}; + +static struct rc_keymap winfast_usbii_deluxe_map = { + .map = { + .scan = winfast_usbii_deluxe, + .size = ARRAY_SIZE(winfast_usbii_deluxe), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_WINFAST_USBII_DELUXE, + } +}; + +static int __init init_rc_map_winfast_usbii_deluxe(void) +{ + return ir_register_map(&winfast_usbii_deluxe_map); +} + +static void __exit exit_rc_map_winfast_usbii_deluxe(void) +{ + ir_unregister_map(&winfast_usbii_deluxe_map); +} + +module_init(init_rc_map_winfast_usbii_deluxe) +module_exit(exit_rc_map_winfast_usbii_deluxe) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-winfast.c b/drivers/media/IR/keymaps/rc-winfast.c new file mode 100644 index 000000000000..0e90a3bd9499 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-winfast.c @@ -0,0 +1,102 @@ +/* winfast.h - Keytable for winfast Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + */ + +#include <media/rc-map.h> + +/* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */ + +static struct ir_scancode winfast[] = { + /* Keys 0 to 9 */ + { 0x12, KEY_0 }, + { 0x05, KEY_1 }, + { 0x06, KEY_2 }, + { 0x07, KEY_3 }, + { 0x09, KEY_4 }, + { 0x0a, KEY_5 }, + { 0x0b, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x0e, KEY_8 }, + { 0x0f, KEY_9 }, + + { 0x00, KEY_POWER }, + { 0x1b, KEY_AUDIO }, /* Audio Source */ + { 0x02, KEY_TUNER }, /* TV/FM, not on Y0400052 */ + { 0x1e, KEY_VIDEO }, /* Video Source */ + { 0x16, KEY_INFO }, /* Display information */ + { 0x04, KEY_VOLUMEUP }, + { 0x08, KEY_VOLUMEDOWN }, + { 0x0c, KEY_CHANNELUP }, + { 0x10, KEY_CHANNELDOWN }, + { 0x03, KEY_ZOOM }, /* fullscreen */ + { 0x1f, KEY_TEXT }, /* closed caption/teletext */ + { 0x20, KEY_SLEEP }, + { 0x29, KEY_CLEAR }, /* boss key */ + { 0x14, KEY_MUTE }, + { 0x2b, KEY_RED }, + { 0x2c, KEY_GREEN }, + { 0x2d, KEY_YELLOW }, + { 0x2e, KEY_BLUE }, + { 0x18, KEY_KPPLUS }, /* fine tune + , not on Y040052 */ + { 0x19, KEY_KPMINUS }, /* fine tune - , not on Y040052 */ + { 0x2a, KEY_MEDIA }, /* PIP (Picture in picture */ + { 0x21, KEY_DOT }, + { 0x13, KEY_ENTER }, + { 0x11, KEY_LAST }, /* Recall (last channel */ + { 0x22, KEY_PREVIOUS }, + { 0x23, KEY_PLAYPAUSE }, + { 0x24, KEY_NEXT }, + { 0x25, KEY_TIME }, /* Time Shifting */ + { 0x26, KEY_STOP }, + { 0x27, KEY_RECORD }, + { 0x28, KEY_SAVE }, /* Screenshot */ + { 0x2f, KEY_MENU }, + { 0x30, KEY_CANCEL }, + { 0x31, KEY_CHANNEL }, /* Channel Surf */ + { 0x32, KEY_SUBTITLE }, + { 0x33, KEY_LANGUAGE }, + { 0x34, KEY_REWIND }, + { 0x35, KEY_FASTFORWARD }, + { 0x36, KEY_TV }, + { 0x37, KEY_RADIO }, /* FM */ + { 0x38, KEY_DVD }, + + { 0x1a, KEY_MODE}, /* change to MCE mode on Y04G0051 */ + { 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */ + { 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */ + { 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */ + { 0x3f, KEY_F24 } /* MCE -CH, on Y04G0033 */ +}; + +static struct rc_keymap winfast_map = { + .map = { + .scan = winfast, + .size = ARRAY_SIZE(winfast), + .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_WINFAST, + } +}; + +static int __init init_rc_map_winfast(void) +{ + return ir_register_map(&winfast_map); +} + +static void __exit exit_rc_map_winfast(void) +{ + ir_unregister_map(&winfast_map); +} + +module_init(init_rc_map_winfast) +module_exit(exit_rc_map_winfast) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/rc-map.c b/drivers/media/IR/rc-map.c new file mode 100644 index 000000000000..46a8f1524b5b --- /dev/null +++ b/drivers/media/IR/rc-map.c @@ -0,0 +1,84 @@ +/* ir-raw-event.c - handle IR Pulse/Space event + * + * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * 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. + * + * 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 <media/ir-core.h> +#include <linux/spinlock.h> +#include <linux/delay.h> + +/* Used to handle IR raw handler extensions */ +static LIST_HEAD(rc_map_list); +static DEFINE_SPINLOCK(rc_map_lock); + +static struct rc_keymap *seek_rc_map(const char *name) +{ + struct rc_keymap *map = NULL; + + spin_lock(&rc_map_lock); + list_for_each_entry(map, &rc_map_list, list) { + if (!strcmp(name, map->map.name)) { + spin_unlock(&rc_map_lock); + return map; + } + } + spin_unlock(&rc_map_lock); + + return NULL; +} + +struct ir_scancode_table *get_rc_map(const char *name) +{ + + struct rc_keymap *map; + + map = seek_rc_map(name); +#ifdef MODULE + if (!map) { + int rc = request_module(name); + if (rc < 0) { + printk(KERN_ERR "Couldn't load IR keymap %s\n", name); + return NULL; + } + msleep(20); /* Give some time for IR to register */ + + map = seek_rc_map(name); + } +#endif + if (!map) { + printk(KERN_ERR "IR keymap %s not found\n", name); + return NULL; + } + + printk(KERN_INFO "Registered IR keymap %s\n", map->map.name); + + return &map->map; +} +EXPORT_SYMBOL_GPL(get_rc_map); + +int ir_register_map(struct rc_keymap *map) +{ + spin_lock(&rc_map_lock); + list_add_tail(&map->list, &rc_map_list); + spin_unlock(&rc_map_lock); + return 0; +} +EXPORT_SYMBOL_GPL(ir_register_map); + +void ir_unregister_map(struct rc_keymap *map) +{ + spin_lock(&rc_map_lock); + list_del(&map->list); + spin_unlock(&rc_map_lock); +} +EXPORT_SYMBOL_GPL(ir_unregister_map); + diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index 96d61707f501..b6ce528e1889 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -100,6 +100,8 @@ struct xc2028_data { if (size != _rc) \ tuner_info("i2c output error: rc = %d (should be %d)\n",\ _rc, (int)size); \ + if (priv->ctrl.msleep) \ + msleep(priv->ctrl.msleep); \ _rc; \ }) @@ -119,6 +121,8 @@ struct xc2028_data { if (isize != _rc) \ tuner_err("i2c input error: rc = %d (should be %d)\n", \ _rc, (int)isize); \ + if (priv->ctrl.msleep) \ + msleep(priv->ctrl.msleep); \ _rc; \ }) @@ -129,8 +133,8 @@ struct xc2028_data { (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \ _val, sizeof(_val)))) { \ tuner_err("Error on line %d: %d\n", __LINE__, _rc); \ - } else \ - msleep(10); \ + } else if (priv->ctrl.msleep) \ + msleep(priv->ctrl.msleep); \ _rc; \ }) @@ -809,10 +813,20 @@ check_device: hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, (version & 0xf0) >> 4, version & 0xf); + + if (priv->ctrl.read_not_reliable) + goto read_not_reliable; + /* Check firmware version against what we downloaded. */ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { - tuner_err("Incorrect readback of firmware version.\n"); - goto fail; + if (!priv->ctrl.read_not_reliable) { + tuner_err("Incorrect readback of firmware version.\n"); + goto fail; + } else { + tuner_err("Returned an incorrect version. However, " + "read is not reliable enough. Ignoring it.\n"); + hwmodel = 3028; + } } /* Check that the tuner hardware model remains consistent over time. */ @@ -826,6 +840,7 @@ check_device: goto fail; } +read_not_reliable: memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); /* @@ -996,6 +1011,8 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, The reset CLK is needed only with tm6000. Driver should work fine even if this fails. */ + if (priv->ctrl.msleep) + msleep(priv->ctrl.msleep); do_tuner_callback(fe, XC2028_RESET_CLK, 1); msleep(10); diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h index a90c35d50add..9778c96a5006 100644 --- a/drivers/media/common/tuners/tuner-xc2028.h +++ b/drivers/media/common/tuners/tuner-xc2028.h @@ -33,12 +33,14 @@ enum firmware_type { struct xc2028_ctrl { char *fname; int max_len; + int msleep; unsigned int scode_table; unsigned int mts :1; unsigned int input1:1; unsigned int vhfbw7:1; unsigned int uhfbw8:1; unsigned int disable_power_mgmt:1; + unsigned int read_not_reliable:1; unsigned int demod; enum firmware_type type:2; }; diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c index 8b0cde38984d..248a2a9d8416 100644 --- a/drivers/media/dvb/bt8xx/dst.c +++ b/drivers/media/dvb/bt8xx/dst.c @@ -930,7 +930,6 @@ static int dst_fw_ver(struct dst_state *state) dprintk(verbose, DST_INFO, 1, "Unsupported Command"); return -1; } - memset(&state->fw_version, '\0', 8); memcpy(&state->fw_version, &state->rxbuffer, 8); dprintk(verbose, DST_ERROR, 1, "Firmware Ver = %x.%x Build = %02x, on %x:%x, %x-%x-20%02x", state->fw_version[0] >> 4, state->fw_version[0] & 0x0f, @@ -1053,7 +1052,6 @@ static int dst_get_tuner_info(struct dst_state *state) goto force; } } - memset(&state->board_info, '\0', 8); memcpy(&state->board_info, &state->rxbuffer, 8); if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { dprintk(verbose, DST_ERROR, 1, "DST type has TS=188"); diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index b6d46961a99e..b762e561a6d5 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -28,7 +28,7 @@ #include <linux/dma-mapping.h> #include <linux/input.h> #include <linux/slab.h> -#include <media/ir-common.h> +#include <media/ir-core.h> #include "demux.h" #include "dmxdev.h" @@ -46,6 +46,8 @@ #include "z0194a.h" #include "ds3000.h" +#define MODULE_NAME "dm1105" + #define UNSET (-1U) #define DM1105_BOARD_NOAUTO UNSET @@ -265,7 +267,6 @@ static void dm1105_card_list(struct pci_dev *pci) /* infrared remote control */ struct infrared { struct input_dev *input_dev; - struct ir_input_state ir; char input_phys[32]; struct work_struct work; u32 ir_command; @@ -531,8 +532,7 @@ static void dm1105_emit_key(struct work_struct *work) data = (ircom >> 8) & 0x7f; - ir_input_keydown(ir->input_dev, &ir->ir, data); - ir_input_nokey(ir->input_dev, &ir->ir); + ir_keydown(ir->input_dev, data, 0); } /* work handler */ @@ -594,8 +594,7 @@ static irqreturn_t dm1105_irq(int irq, void *dev_id) int __devinit dm1105_ir_init(struct dm1105_dev *dm1105) { struct input_dev *input_dev; - struct ir_scancode_table *ir_codes = &ir_codes_dm1105_nec_table; - u64 ir_type = IR_TYPE_OTHER; + char *ir_codes = NULL; int err = -ENOMEM; input_dev = input_allocate_device(); @@ -606,12 +605,6 @@ int __devinit dm1105_ir_init(struct dm1105_dev *dm1105) snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), "pci-%s/ir0", pci_name(dm1105->pdev)); - err = ir_input_init(input_dev, &dm1105->ir.ir, ir_type); - if (err < 0) { - input_free_device(input_dev); - return err; - } - input_dev->name = "DVB on-card IR receiver"; input_dev->phys = dm1105->ir.input_phys; input_dev->id.bustype = BUS_PCI; @@ -628,9 +621,13 @@ int __devinit dm1105_ir_init(struct dm1105_dev *dm1105) INIT_WORK(&dm1105->ir.work, dm1105_emit_key); - err = ir_input_register(input_dev, ir_codes, NULL); + err = ir_input_register(input_dev, ir_codes, NULL, MODULE_NAME); + if (err < 0) { + input_free_device(input_dev); + return err; + } - return err; + return 0; } void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105) diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index 67f189b7aa1f..977ddba3e235 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -426,7 +426,7 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) }; }; - if (demux->cnt_storage) { + if (demux->cnt_storage && dvb_demux_tscheck) { /* check pkt counter */ if (pid < MAX_PID) { if (buf[1] & 0x80) @@ -1248,12 +1248,9 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux) dvbdemux->feed[i].index = i; } - if (dvb_demux_tscheck) { - dvbdemux->cnt_storage = vmalloc(MAX_PID + 1); - - if (!dvbdemux->cnt_storage) - printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n"); - } + dvbdemux->cnt_storage = vmalloc(MAX_PID + 1); + if (!dvbdemux->cnt_storage) + printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n"); INIT_LIST_HEAD(&dvbdemux->frontend_list); diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 55ea260572bf..6932def4d266 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -95,6 +95,10 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open( * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again. */ +#define DVB_FE_NO_EXIT 0 +#define DVB_FE_NORMAL_EXIT 1 +#define DVB_FE_DEVICE_REMOVED 2 + static DEFINE_MUTEX(frontend_mutex); struct dvb_frontend_private { @@ -497,7 +501,7 @@ static int dvb_frontend_is_exiting(struct dvb_frontend *fe) { struct dvb_frontend_private *fepriv = fe->frontend_priv; - if (fepriv->exit) + if (fepriv->exit != DVB_FE_NO_EXIT) return 1; if (fepriv->dvbdev->writers == 1) @@ -559,7 +563,7 @@ restart: if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { /* got signal or quitting */ - fepriv->exit = 1; + fepriv->exit = DVB_FE_NORMAL_EXIT; break; } @@ -673,7 +677,10 @@ restart: } fepriv->thread = NULL; - fepriv->exit = 0; + if (kthread_should_stop()) + fepriv->exit = DVB_FE_DEVICE_REMOVED; + else + fepriv->exit = DVB_FE_NO_EXIT; mb(); dvb_frontend_wakeup(fe); @@ -686,7 +693,7 @@ static void dvb_frontend_stop(struct dvb_frontend *fe) dprintk ("%s\n", __func__); - fepriv->exit = 1; + fepriv->exit = DVB_FE_NORMAL_EXIT; mb(); if (!fepriv->thread) @@ -755,7 +762,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe) dprintk ("%s\n", __func__); if (fepriv->thread) { - if (!fepriv->exit) + if (fepriv->exit == DVB_FE_NO_EXIT) return 0; else dvb_frontend_stop (fe); @@ -767,7 +774,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe) return -EINTR; fepriv->state = FESTATE_IDLE; - fepriv->exit = 0; + fepriv->exit = DVB_FE_NO_EXIT; fepriv->thread = NULL; mb(); @@ -1490,7 +1497,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, dprintk("%s (%d)\n", __func__, _IOC_NR(cmd)); - if (fepriv->exit) + if (fepriv->exit != DVB_FE_NO_EXIT) return -ENODEV; if ((file->f_flags & O_ACCMODE) == O_RDONLY && @@ -1916,6 +1923,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) int ret; dprintk ("%s\n", __func__); + if (fepriv->exit == DVB_FE_DEVICE_REMOVED) + return -ENODEV; if (adapter->mfe_shared) { mutex_lock (&adapter->mfe_lock); @@ -2008,7 +2017,7 @@ static int dvb_frontend_release(struct inode *inode, struct file *file) ret = dvb_generic_release (inode, file); if (dvbdev->users == -1) { - if (fepriv->exit == 1) { + if (fepriv->exit != DVB_FE_NO_EXIT) { fops_put(file->f_op); file->f_op = NULL; wake_up(&dvbdev->wait_queue); diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index 6247239982e9..b6cbb1dfc5f1 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -37,7 +37,7 @@ static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_pr return 0; } -static struct dvb_usb_rc_key a800_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_a800_table[] = { { 0x0201, KEY_PROG1 }, /* SOURCE */ { 0x0200, KEY_POWER }, /* POWER */ { 0x0205, KEY_1 }, /* 1 */ @@ -147,8 +147,8 @@ static struct dvb_usb_device_properties a800_properties = { .identify_state = a800_identify_state, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = a800_rc_keys, - .rc_key_map_size = ARRAY_SIZE(a800_rc_keys), + .rc_key_map = ir_codes_a800_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_a800_table), .rc_query = a800_rc_query, .i2c_algo = &dibusb_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/dvb/dvb-usb/af9005-remote.c index f4379c650a19..b41fa873b04d 100644 --- a/drivers/media/dvb/dvb-usb/af9005-remote.c +++ b/drivers/media/dvb/dvb-usb/af9005-remote.c @@ -33,7 +33,7 @@ MODULE_PARM_DESC(debug, #define deb_decode(args...) dprintk(dvb_usb_af9005_remote_debug,0x01,args) -struct dvb_usb_rc_key af9005_rc_keys[] = { +struct dvb_usb_rc_key ir_codes_af9005_table[] = { {0x01b7, KEY_POWER}, {0x01a7, KEY_VOLUMEUP}, @@ -74,7 +74,7 @@ struct dvb_usb_rc_key af9005_rc_keys[] = { {0x00d5, KEY_GOTO}, /* marked jump on the remote */ }; -int af9005_rc_keys_size = ARRAY_SIZE(af9005_rc_keys); +int ir_codes_af9005_table_size = ARRAY_SIZE(ir_codes_af9005_table); static int repeatable_keys[] = { KEY_VOLUMEUP, @@ -130,10 +130,10 @@ int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, deb_decode("code != inverted code\n"); return 0; } - for (i = 0; i < af9005_rc_keys_size; i++) { - if (rc5_custom(&af9005_rc_keys[i]) == cust - && rc5_data(&af9005_rc_keys[i]) == dat) { - *event = af9005_rc_keys[i].event; + for (i = 0; i < ir_codes_af9005_table_size; i++) { + if (rc5_custom(&ir_codes_af9005_table[i]) == cust + && rc5_data(&ir_codes_af9005_table[i]) == dat) { + *event = ir_codes_af9005_table[i].event; *state = REMOTE_KEY_PRESSED; deb_decode ("key pressed, event %x\n", *event); @@ -146,8 +146,8 @@ int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, return 0; } -EXPORT_SYMBOL(af9005_rc_keys); -EXPORT_SYMBOL(af9005_rc_keys_size); +EXPORT_SYMBOL(ir_codes_af9005_table); +EXPORT_SYMBOL(ir_codes_af9005_table_size); EXPORT_SYMBOL(af9005_rc_decode); MODULE_AUTHOR("Luca Olivetti <luca@ventoso.org>"); diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c index ca5a0a4d2a47..cfd6107d5349 100644 --- a/drivers/media/dvb/dvb-usb/af9005.c +++ b/drivers/media/dvb/dvb-usb/af9005.c @@ -1109,8 +1109,8 @@ static int __init af9005_usb_module_init(void) return result; } rc_decode = symbol_request(af9005_rc_decode); - rc_keys = symbol_request(af9005_rc_keys); - rc_keys_size = symbol_request(af9005_rc_keys_size); + rc_keys = symbol_request(ir_codes_af9005_table); + rc_keys_size = symbol_request(ir_codes_af9005_table_size); if (rc_decode == NULL || rc_keys == NULL || rc_keys_size == NULL) { err("af9005_rc_decode function not found, disabling remote"); af9005_properties.rc_query = NULL; @@ -1128,9 +1128,9 @@ static void __exit af9005_usb_module_exit(void) if (rc_decode != NULL) symbol_put(af9005_rc_decode); if (rc_keys != NULL) - symbol_put(af9005_rc_keys); + symbol_put(ir_codes_af9005_table); if (rc_keys_size != NULL) - symbol_put(af9005_rc_keys_size); + symbol_put(ir_codes_af9005_table_size); /* deregister this driver from the USB subsystem */ usb_deregister(&af9005_usb_driver); } diff --git a/drivers/media/dvb/dvb-usb/af9005.h b/drivers/media/dvb/dvb-usb/af9005.h index 0bc48a012187..088e7083a39b 100644 --- a/drivers/media/dvb/dvb-usb/af9005.h +++ b/drivers/media/dvb/dvb-usb/af9005.h @@ -3490,7 +3490,7 @@ extern u8 regmask[8]; /* remote control decoder */ extern int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, int *state); -extern struct dvb_usb_rc_key af9005_rc_keys[]; -extern int af9005_rc_keys_size; +extern struct dvb_usb_rc_key ir_codes_af9005_table[]; +extern int ir_codes_af9005_table_size; #endif diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 74d94e45324d..66c7c3ea7990 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -752,19 +752,19 @@ static const struct af9015_setup *af9015_setup_match(unsigned int id, static const struct af9015_setup af9015_setup_modparam[] = { { AF9015_REMOTE_A_LINK_DTU_M, - af9015_rc_keys_a_link, ARRAY_SIZE(af9015_rc_keys_a_link), + ir_codes_af9015_table_a_link, ARRAY_SIZE(ir_codes_af9015_table_a_link), af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) }, { AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, - af9015_rc_keys_msi, ARRAY_SIZE(af9015_rc_keys_msi), + ir_codes_af9015_table_msi, ARRAY_SIZE(ir_codes_af9015_table_msi), af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) }, { AF9015_REMOTE_MYGICTV_U718, - af9015_rc_keys_mygictv, ARRAY_SIZE(af9015_rc_keys_mygictv), + ir_codes_af9015_table_mygictv, ARRAY_SIZE(ir_codes_af9015_table_mygictv), af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) }, { AF9015_REMOTE_DIGITTRADE_DVB_T, - af9015_rc_keys_digittrade, ARRAY_SIZE(af9015_rc_keys_digittrade), + ir_codes_af9015_table_digittrade, ARRAY_SIZE(ir_codes_af9015_table_digittrade), af9015_ir_table_digittrade, ARRAY_SIZE(af9015_ir_table_digittrade) }, { AF9015_REMOTE_AVERMEDIA_KS, - af9015_rc_keys_avermedia, ARRAY_SIZE(af9015_rc_keys_avermedia), + ir_codes_af9015_table_avermedia, ARRAY_SIZE(ir_codes_af9015_table_avermedia), af9015_ir_table_avermedia_ks, ARRAY_SIZE(af9015_ir_table_avermedia_ks) }, { } }; @@ -772,32 +772,32 @@ static const struct af9015_setup af9015_setup_modparam[] = { /* don't add new entries here anymore, use hashes instead */ static const struct af9015_setup af9015_setup_usbids[] = { { USB_VID_LEADTEK, - af9015_rc_keys_leadtek, ARRAY_SIZE(af9015_rc_keys_leadtek), + ir_codes_af9015_table_leadtek, ARRAY_SIZE(ir_codes_af9015_table_leadtek), af9015_ir_table_leadtek, ARRAY_SIZE(af9015_ir_table_leadtek) }, { USB_VID_VISIONPLUS, - af9015_rc_keys_twinhan, ARRAY_SIZE(af9015_rc_keys_twinhan), + ir_codes_af9015_table_twinhan, ARRAY_SIZE(ir_codes_af9015_table_twinhan), af9015_ir_table_twinhan, ARRAY_SIZE(af9015_ir_table_twinhan) }, { USB_VID_KWORLD_2, /* TODO: use correct rc keys */ - af9015_rc_keys_twinhan, ARRAY_SIZE(af9015_rc_keys_twinhan), + ir_codes_af9015_table_twinhan, ARRAY_SIZE(ir_codes_af9015_table_twinhan), af9015_ir_table_kworld, ARRAY_SIZE(af9015_ir_table_kworld) }, { USB_VID_AVERMEDIA, - af9015_rc_keys_avermedia, ARRAY_SIZE(af9015_rc_keys_avermedia), + ir_codes_af9015_table_avermedia, ARRAY_SIZE(ir_codes_af9015_table_avermedia), af9015_ir_table_avermedia, ARRAY_SIZE(af9015_ir_table_avermedia) }, { USB_VID_MSI_2, - af9015_rc_keys_msi_digivox_iii, ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii), + ir_codes_af9015_table_msi_digivox_iii, ARRAY_SIZE(ir_codes_af9015_table_msi_digivox_iii), af9015_ir_table_msi_digivox_iii, ARRAY_SIZE(af9015_ir_table_msi_digivox_iii) }, { } }; static const struct af9015_setup af9015_setup_hashes[] = { { 0xb8feb708, - af9015_rc_keys_msi, ARRAY_SIZE(af9015_rc_keys_msi), + ir_codes_af9015_table_msi, ARRAY_SIZE(ir_codes_af9015_table_msi), af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) }, { 0xa3703d00, - af9015_rc_keys_a_link, ARRAY_SIZE(af9015_rc_keys_a_link), + ir_codes_af9015_table_a_link, ARRAY_SIZE(ir_codes_af9015_table_a_link), af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) }, { 0x9b7dc64e, - af9015_rc_keys_mygictv, ARRAY_SIZE(af9015_rc_keys_mygictv), + ir_codes_af9015_table_mygictv, ARRAY_SIZE(ir_codes_af9015_table_mygictv), af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) }, { } }; @@ -836,8 +836,8 @@ static void af9015_set_remote_config(struct usb_device *udev, } else if (udev->descriptor.idProduct == cpu_to_le16(USB_PID_TREKSTOR_DVBT)) { table = &(const struct af9015_setup){ 0, - af9015_rc_keys_trekstor, - ARRAY_SIZE(af9015_rc_keys_trekstor), + ir_codes_af9015_table_trekstor, + ARRAY_SIZE(ir_codes_af9015_table_trekstor), af9015_ir_table_trekstor, ARRAY_SIZE(af9015_ir_table_trekstor) }; @@ -1297,6 +1297,8 @@ static struct usb_device_id af9015_usb_table[] = { {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)}, {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)}, +/* 30 */{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1500,7 +1502,8 @@ static struct dvb_usb_device_properties af9015_properties[] = { "(VS-DVB-T 395U)", .cold_ids = {&af9015_usb_table[16], &af9015_usb_table[17], - &af9015_usb_table[18], NULL}, + &af9015_usb_table[18], + &af9015_usb_table[31], NULL}, .warm_ids = {NULL}, }, { @@ -1569,7 +1572,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .i2c_algo = &af9015_i2c_algo, - .num_device_descs = 7, /* max 9 */ + .num_device_descs = 8, /* max 9 */ .devices = { { .name = "AverMedia AVerTV Volar GPS 805 (A805)", @@ -1608,6 +1611,12 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = {&af9015_usb_table[29], NULL}, .warm_ids = {NULL}, }, + { + .name = "KWorld USB DVB-T Stick Mobile " \ + "(UB383-T)", + .cold_ids = {&af9015_usb_table[30], NULL}, + .warm_ids = {NULL}, + }, } }, }; diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index ef36b1831490..63b2a4907b7e 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -123,7 +123,7 @@ enum af9015_remote { /* LeadTek - Y04G0051 */ /* Leadtek WinFast DTV Dongle Gold */ -static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_leadtek[] = { { 0x001e, KEY_1 }, { 0x001f, KEY_2 }, { 0x0020, KEY_3 }, @@ -227,7 +227,7 @@ static u8 af9015_ir_table_leadtek[] = { }; /* TwinHan AzureWave AD-TU700(704J) */ -static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_twinhan[] = { { 0x053f, KEY_POWER }, { 0x0019, KEY_FAVORITES }, /* Favorite List */ { 0x0004, KEY_TEXT }, /* Teletext */ @@ -338,7 +338,7 @@ static u8 af9015_ir_table_twinhan[] = { }; /* A-Link DTU(m) */ -static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_a_link[] = { { 0x001e, KEY_1 }, { 0x001f, KEY_2 }, { 0x0020, KEY_3 }, @@ -381,7 +381,7 @@ static u8 af9015_ir_table_a_link[] = { }; /* MSI DIGIVOX mini II V3.0 */ -static struct dvb_usb_rc_key af9015_rc_keys_msi[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_msi[] = { { 0x001e, KEY_1 }, { 0x001f, KEY_2 }, { 0x0020, KEY_3 }, @@ -424,7 +424,7 @@ static u8 af9015_ir_table_msi[] = { }; /* MYGICTV U718 */ -static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_mygictv[] = { { 0x003d, KEY_SWITCHVIDEOMODE }, /* TV / AV */ { 0x0545, KEY_POWER }, @@ -550,7 +550,7 @@ static u8 af9015_ir_table_kworld[] = { }; /* AverMedia Volar X */ -static struct dvb_usb_rc_key af9015_rc_keys_avermedia[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_avermedia[] = { { 0x053d, KEY_PROG1 }, /* SOURCE */ { 0x0512, KEY_POWER }, /* POWER */ { 0x051e, KEY_1 }, /* 1 */ @@ -656,7 +656,7 @@ static u8 af9015_ir_table_avermedia_ks[] = { }; /* Digittrade DVB-T USB Stick */ -static struct dvb_usb_rc_key af9015_rc_keys_digittrade[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_digittrade[] = { { 0x010f, KEY_LAST }, /* RETURN */ { 0x0517, KEY_TEXT }, /* TELETEXT */ { 0x0108, KEY_EPG }, /* EPG */ @@ -719,7 +719,7 @@ static u8 af9015_ir_table_digittrade[] = { }; /* TREKSTOR DVB-T USB Stick */ -static struct dvb_usb_rc_key af9015_rc_keys_trekstor[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_trekstor[] = { { 0x0704, KEY_AGAIN }, /* Home */ { 0x0705, KEY_MUTE }, /* Mute */ { 0x0706, KEY_UP }, /* Up */ @@ -782,7 +782,7 @@ static u8 af9015_ir_table_trekstor[] = { }; /* MSI DIGIVOX mini III */ -static struct dvb_usb_rc_key af9015_rc_keys_msi_digivox_iii[] = { +static struct dvb_usb_rc_key ir_codes_af9015_table_msi_digivox_iii[] = { { 0x0713, KEY_POWER }, /* [red power button] */ { 0x073b, KEY_VIDEO }, /* Source */ { 0x073e, KEY_ZOOM }, /* Zoom */ diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index bb69f3719f9a..faca1ad88a67 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -399,7 +399,7 @@ static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return 0; } -static struct dvb_usb_rc_key anysee_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_anysee_table[] = { { 0x0100, KEY_0 }, { 0x0101, KEY_1 }, { 0x0102, KEY_2 }, @@ -518,8 +518,8 @@ static struct dvb_usb_device_properties anysee_properties = { } }, - .rc_key_map = anysee_rc_keys, - .rc_key_map_size = ARRAY_SIZE(anysee_rc_keys), + .rc_key_map = ir_codes_anysee_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_anysee_table), .rc_query = anysee_rc_query, .rc_interval = 200, /* windows driver uses 500ms */ diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index d7290b2c0913..6681ac1c56e3 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -125,12 +125,12 @@ static const struct stb0899_s1_reg az6027_stb0899_s1_init_3[] = { { STB0899_RCOMPC , 0xc9 }, { STB0899_AGC1CN , 0x01 }, { STB0899_AGC1REF , 0x10 }, - { STB0899_RTC , 0x23 }, + { STB0899_RTC , 0x23 }, { STB0899_TMGCFG , 0x4e }, { STB0899_AGC2REF , 0x34 }, { STB0899_TLSR , 0x84 }, { STB0899_CFD , 0xf7 }, - { STB0899_ACLC , 0x87 }, + { STB0899_ACLC , 0x87 }, { STB0899_BCLC , 0x94 }, { STB0899_EQON , 0x41 }, { STB0899_LDT , 0xf1 }, @@ -183,10 +183,10 @@ static const struct stb0899_s1_reg az6027_stb0899_s1_init_3[] = { { STB0899_ECNT3M , 0x0a }, { STB0899_ECNT3L , 0xad }, { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, + { STB0899_FECM , 0x01 }, { STB0899_VTH12 , 0xb0 }, { STB0899_VTH23 , 0x7a }, - { STB0899_VTH34 , 0x58 }, + { STB0899_VTH34 , 0x58 }, { STB0899_VTH56 , 0x38 }, { STB0899_VTH67 , 0x34 }, { STB0899_VTH78 , 0x24 }, @@ -195,7 +195,7 @@ static const struct stb0899_s1_reg az6027_stb0899_s1_init_3[] = { { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ { STB0899_TSULC , 0x42 }, { STB0899_RSLLC , 0x41 }, - { STB0899_TSLPL , 0x12 }, + { STB0899_TSLPL , 0x12 }, { STB0899_TSCFGH , 0x0c }, { STB0899_TSCFGM , 0x00 }, { STB0899_TSCFGL , 0x00 }, @@ -386,7 +386,7 @@ static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } /* keys for the enclosed remote control */ -static struct dvb_usb_rc_key az6027_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_az6027_table[] = { { 0x01, KEY_1 }, { 0x02, KEY_2 }, }; @@ -417,11 +417,15 @@ static int az6027_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, u16 value; u16 index; int blen; - u8 b[12]; + u8 *b; if (slot != 0) return -EINVAL; + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + mutex_lock(&state->ca_mutex); req = 0xC1; @@ -438,6 +442,7 @@ static int az6027_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, } mutex_unlock(&state->ca_mutex); + kfree(b); return ret; } @@ -485,11 +490,15 @@ static int az6027_ci_read_cam_control(struct dvb_ca_en50221 *ca, u16 value; u16 index; int blen; - u8 b[12]; + u8 *b; if (slot != 0) return -EINVAL; + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + mutex_lock(&state->ca_mutex); req = 0xC3; @@ -510,6 +519,7 @@ static int az6027_ci_read_cam_control(struct dvb_ca_en50221 *ca, } mutex_unlock(&state->ca_mutex); + kfree(b); return ret; } @@ -556,7 +566,11 @@ static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) u16 value; u16 index; int blen; - u8 b[12]; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; req = 0xC8; value = 0; @@ -570,6 +584,7 @@ static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) } else{ ret = b[0]; } + kfree(b); return ret; } @@ -667,8 +682,11 @@ static int az6027_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int o u16 value; u16 index; int blen; - u8 b[12]; + u8 *b; + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; mutex_lock(&state->ca_mutex); req = 0xC5; @@ -683,15 +701,13 @@ static int az6027_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int o } else ret = 0; - if (b[0] == 0) { - ret = 0; - - } else if (b[0] == 1) { + if (!ret && b[0] == 1) { ret = DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; } mutex_unlock(&state->ca_mutex); + kfree(b); return ret; } @@ -943,10 +959,16 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n u16 value; int length; u8 req; - u8 data[256]; + u8 *data; - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + data = kmalloc(256, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) { + kfree(data); return -EAGAIN; + } if (num > 2) warn("more than 2 i2c messages at a time is not handled yet. TODO."); @@ -976,17 +998,14 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n i++; } else { - if (msg[i].addr == 0xd0) { - /* demod 16bit addr */ - req = 0xBD; - index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); - value = msg[i].addr + (2 << 8); - length = msg[i].len - 2; - len = msg[i].len - 2; - for (j = 0; j < len; j++) - data[j] = msg[i].buf[j + 2]; - - } + /* demod 16bit addr */ + req = 0xBD; + index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); + value = msg[i].addr + (2 << 8); + length = msg[i].len - 2; + len = msg[i].len - 2; + for (j = 0; j < len; j++) + data[j] = msg[i].buf[j + 2]; az6027_usb_out_op(d, req, value, index, data, length); } } @@ -1019,6 +1038,7 @@ static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int n } } mutex_unlock(&d->i2c_mutex); + kfree(data); return i; } @@ -1039,8 +1059,14 @@ int az6027_identify_state(struct usb_device *udev, struct dvb_usb_device_description **desc, int *cold) { - u8 b[16]; - s16 ret = usb_control_msg(udev, + u8 *b; + s16 ret; + + b = kmalloc(16, GFP_KERNEL); + if (!b) + return -ENOMEM; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xb7, USB_TYPE_VENDOR | USB_DIR_IN, @@ -1051,7 +1077,7 @@ int az6027_identify_state(struct usb_device *udev, USB_CTRL_GET_TIMEOUT); *cold = ret <= 0; - + kfree(b); deb_info("cold: %d\n", *cold); return 0; } @@ -1059,8 +1085,10 @@ int az6027_identify_state(struct usb_device *udev, static struct usb_device_id az6027_usb_table[] = { { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_AZ6027) }, - { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI) }, - { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI_V1) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI_V2) }, + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V1) }, + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V2) }, { }, }; @@ -1097,18 +1125,34 @@ static struct dvb_usb_device_properties az6027_properties = { .power_ctrl = az6027_power_ctrl, .read_mac_address = az6027_read_mac_addr, */ - .rc_key_map = az6027_rc_keys, - .rc_key_map_size = ARRAY_SIZE(az6027_rc_keys), + .rc_key_map = ir_codes_az6027_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_az6027_table), .rc_interval = 400, .rc_query = az6027_rc_query, .i2c_algo = &az6027_i2c_algo, - .num_device_descs = 1, + .num_device_descs = 5, .devices = { { .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", .cold_ids = { &az6027_usb_table[0], NULL }, .warm_ids = { NULL }, + }, { + .name = "TERRATEC S7", + .cold_ids = { &az6027_usb_table[1], NULL }, + .warm_ids = { NULL }, + }, { + .name = "TERRATEC S7 MKII", + .cold_ids = { &az6027_usb_table[2], NULL }, + .warm_ids = { NULL }, + }, { + .name = "Technisat SkyStar USB 2 HD CI", + .cold_ids = { &az6027_usb_table[3], NULL }, + .warm_ids = { NULL }, + }, { + .name = "Technisat SkyStar USB 2 HD CI", + .cold_ids = { &az6027_usb_table[4], NULL }, + .warm_ids = { NULL }, }, { NULL }, } diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c index e37ac4d48602..5a9c14bdc980 100644 --- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c +++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c @@ -84,7 +84,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) return 0; } -static struct dvb_usb_rc_key cinergyt2_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_cinergyt2_table[] = { { 0x0401, KEY_POWER }, { 0x0402, KEY_1 }, { 0x0403, KEY_2 }, @@ -218,8 +218,8 @@ static struct dvb_usb_device_properties cinergyt2_properties = { .power_ctrl = cinergyt2_power_ctrl, .rc_interval = 50, - .rc_key_map = cinergyt2_rc_keys, - .rc_key_map_size = ARRAY_SIZE(cinergyt2_rc_keys), + .rc_key_map = ir_codes_cinergyt2_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_cinergyt2_table), .rc_query = cinergyt2_rc_query, .generic_bulk_ctrl_endpoint = 1, diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 960376da7d59..0eb490889162 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -461,7 +461,7 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, return 0; } -static struct dvb_usb_rc_key dvico_mce_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_dvico_mce_table[] = { { 0xfe02, KEY_TV }, { 0xfe0e, KEY_MP3 }, { 0xfe1a, KEY_DVD }, @@ -509,7 +509,7 @@ static struct dvb_usb_rc_key dvico_mce_rc_keys[] = { { 0xfe4e, KEY_POWER }, }; -static struct dvb_usb_rc_key dvico_portable_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_dvico_portable_table[] = { { 0xfc02, KEY_SETUP }, /* Profile */ { 0xfc43, KEY_POWER2 }, { 0xfc06, KEY_EPG }, @@ -548,7 +548,7 @@ static struct dvb_usb_rc_key dvico_portable_rc_keys[] = { { 0xfc00, KEY_UNKNOWN }, /* HD */ }; -static struct dvb_usb_rc_key d680_dmb_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_d680_dmb_table[] = { { 0x0038, KEY_UNKNOWN }, /* TV/AV */ { 0x080c, KEY_ZOOM }, { 0x0800, KEY_0 }, @@ -1025,8 +1025,9 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); - dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, - &cxusb_dualdig4_rev2_config); + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, + &cxusb_dualdig4_rev2_config) < 0) + return -ENODEV; adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &cxusb_dualdig4_rev2_config); @@ -1449,8 +1450,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { .i2c_algo = &cxusb_i2c_algo, .rc_interval = 100, - .rc_key_map = dvico_portable_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), .rc_query = cxusb_rc_query, .generic_bulk_ctrl_endpoint = 0x01, @@ -1500,8 +1501,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { .i2c_algo = &cxusb_i2c_algo, .rc_interval = 150, - .rc_key_map = dvico_mce_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dvico_mce_rc_keys), + .rc_key_map = ir_codes_dvico_mce_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), .rc_query = cxusb_rc_query, .generic_bulk_ctrl_endpoint = 0x01, @@ -1559,8 +1560,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { .i2c_algo = &cxusb_i2c_algo, .rc_interval = 100, - .rc_key_map = dvico_portable_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), .rc_query = cxusb_rc_query, .generic_bulk_ctrl_endpoint = 0x01, @@ -1609,8 +1610,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { .i2c_algo = &cxusb_i2c_algo, .rc_interval = 100, - .rc_key_map = dvico_portable_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), .rc_query = cxusb_rc_query, .generic_bulk_ctrl_endpoint = 0x01, @@ -1658,8 +1659,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { .generic_bulk_ctrl_endpoint = 0x01, .rc_interval = 100, - .rc_key_map = dvico_mce_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dvico_mce_rc_keys), + .rc_key_map = ir_codes_dvico_mce_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), .rc_query = cxusb_bluebird2_rc_query, .num_device_descs = 1, @@ -1706,8 +1707,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { .generic_bulk_ctrl_endpoint = 0x01, .rc_interval = 100, - .rc_key_map = dvico_portable_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), .rc_query = cxusb_bluebird2_rc_query, .num_device_descs = 1, @@ -1756,8 +1757,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope .generic_bulk_ctrl_endpoint = 0x01, .rc_interval = 100, - .rc_key_map = dvico_portable_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), .rc_query = cxusb_rc_query, .num_device_descs = 1, @@ -1847,8 +1848,8 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { .generic_bulk_ctrl_endpoint = 0x01, .rc_interval = 100, - .rc_key_map = dvico_mce_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dvico_mce_rc_keys), + .rc_key_map = ir_codes_dvico_mce_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), .rc_query = cxusb_rc_query, .num_device_descs = 1, @@ -1895,8 +1896,8 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { .generic_bulk_ctrl_endpoint = 0x01, .rc_interval = 100, - .rc_key_map = d680_dmb_rc_keys, - .rc_key_map_size = ARRAY_SIZE(d680_dmb_rc_keys), + .rc_key_map = ir_codes_d680_dmb_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_d680_dmb_table), .rc_query = cxusb_d680_dmb_rc_query, .num_device_descs = 1, @@ -1944,8 +1945,8 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { .generic_bulk_ctrl_endpoint = 0x01, .rc_interval = 100, - .rc_key_map = d680_dmb_rc_keys, - .rc_key_map_size = ARRAY_SIZE(d680_dmb_rc_keys), + .rc_key_map = ir_codes_d680_dmb_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_d680_dmb_table), .rc_query = cxusb_d680_dmb_rc_query, .num_device_descs = 1, diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 34eab05afc6c..800800a9649e 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -562,7 +562,7 @@ static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return 0; } -static struct dvb_usb_rc_key dib0700_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_dib0700_table[] = { /* Key codes for the tiny Pinnacle remote*/ { 0x0700, KEY_MUTE }, { 0x0701, KEY_MENU }, /* Pinnacle logo */ @@ -794,6 +794,43 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = { { 0x7a13, KEY_VOLUMEDOWN }, { 0x7a40, KEY_POWER }, { 0x7a41, KEY_MUTE }, + + /* Key codes for the Elgato EyeTV Diversity silver remote, + set dvb_usb_dib0700_ir_proto=0 */ + { 0x4501, KEY_POWER }, + { 0x4502, KEY_MUTE }, + { 0x4503, KEY_1 }, + { 0x4504, KEY_2 }, + { 0x4505, KEY_3 }, + { 0x4506, KEY_4 }, + { 0x4507, KEY_5 }, + { 0x4508, KEY_6 }, + { 0x4509, KEY_7 }, + { 0x450a, KEY_8 }, + { 0x450b, KEY_9 }, + { 0x450c, KEY_LAST }, + { 0x450d, KEY_0 }, + { 0x450e, KEY_ENTER }, + { 0x450f, KEY_RED }, + { 0x4510, KEY_CHANNELUP }, + { 0x4511, KEY_GREEN }, + { 0x4512, KEY_VOLUMEDOWN }, + { 0x4513, KEY_OK }, + { 0x4514, KEY_VOLUMEUP }, + { 0x4515, KEY_YELLOW }, + { 0x4516, KEY_CHANNELDOWN }, + { 0x4517, KEY_BLUE }, + { 0x4518, KEY_LEFT }, /* Skip backwards */ + { 0x4519, KEY_PLAYPAUSE }, + { 0x451a, KEY_RIGHT }, /* Skip forward */ + { 0x451b, KEY_REWIND }, + { 0x451c, KEY_L }, /* Live */ + { 0x451d, KEY_FASTFORWARD }, + { 0x451e, KEY_STOP }, /* 'Reveal' for Teletext */ + { 0x451f, KEY_MENU }, /* KEY_TEXT for Teletext */ + { 0x4540, KEY_RECORD }, /* Font 'Size' for Teletext */ + { 0x4541, KEY_SCREEN }, /* Full screen toggle, 'Hold' for Teletext */ + { 0x4542, KEY_SELECT }, /* Select video input, 'Select' for Teletext */ }; /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ @@ -2049,6 +2086,7 @@ struct usb_device_id dib0700_usb_id_table[] = { /* 65 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) }, { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK8096GP) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DIVERSITY) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -2131,8 +2169,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -2160,8 +2198,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -2214,8 +2252,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -2251,8 +2289,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -2321,8 +2359,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -2360,8 +2398,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -2393,7 +2431,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .num_device_descs = 6, + .num_device_descs = 7, .devices = { { "DiBcom STK7070PD reference design", { &dib0700_usb_id_table[17], NULL }, @@ -2419,11 +2457,15 @@ struct dvb_usb_device_properties dib0700_devices[] = { { "Sony PlayTV", { &dib0700_usb_id_table[44], NULL }, { NULL }, - } + }, + { "Elgato EyeTV Diversity", + { &dib0700_usb_id_table[68], NULL }, + { NULL }, + }, }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -2484,8 +2526,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -2513,8 +2555,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -2574,8 +2616,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -2612,8 +2654,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -2656,8 +2698,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -2687,8 +2729,8 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dib0700_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys), + .rc_key_map = ir_codes_dib0700_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), .rc_query = dib0700_rc_query }, }; diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c index 9143b5631e88..bc08bc0b723c 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -327,7 +327,7 @@ EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); /* * common remote control stuff */ -struct dvb_usb_rc_key dibusb_rc_keys[] = { +struct dvb_usb_rc_key ir_codes_dibusb_table[] = { /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ { 0x0016, KEY_POWER }, { 0x0010, KEY_MUTE }, @@ -456,7 +456,7 @@ struct dvb_usb_rc_key dibusb_rc_keys[] = { { 0x804e, KEY_ENTER }, { 0x804f, KEY_VOLUMEDOWN }, }; -EXPORT_SYMBOL(dibusb_rc_keys); +EXPORT_SYMBOL(ir_codes_dibusb_table); int dibusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index 5c0126dc1ff9..eb2e6f050fbe 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -212,7 +212,7 @@ static struct dvb_usb_device_properties dibusb1_1_properties = { .power_ctrl = dibusb_power_ctrl, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dibusb_rc_keys, + .rc_key_map = ir_codes_dibusb_table, .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ .rc_query = dibusb_rc_query, @@ -296,7 +296,7 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = { .power_ctrl = dibusb_power_ctrl, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dibusb_rc_keys, + .rc_key_map = ir_codes_dibusb_table, .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ .rc_query = dibusb_rc_query, @@ -360,7 +360,7 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = { .power_ctrl = dibusb2_0_power_ctrl, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dibusb_rc_keys, + .rc_key_map = ir_codes_dibusb_table, .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ .rc_query = dibusb_rc_query, @@ -417,7 +417,7 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = { .power_ctrl = dibusb2_0_power_ctrl, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dibusb_rc_keys, + .rc_key_map = ir_codes_dibusb_table, .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ .rc_query = dibusb_rc_query, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c index a05b9f875663..588308eb6638 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mc.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -82,7 +82,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = { .power_ctrl = dibusb2_0_power_ctrl, .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = dibusb_rc_keys, + .rc_key_map = ir_codes_dibusb_table, .rc_key_map_size = 111, /* FIXME */ .rc_query = dibusb_rc_query, diff --git a/drivers/media/dvb/dvb-usb/dibusb.h b/drivers/media/dvb/dvb-usb/dibusb.h index 8e847aa73ba1..3d50ac59088f 100644 --- a/drivers/media/dvb/dvb-usb/dibusb.h +++ b/drivers/media/dvb/dvb-usb/dibusb.h @@ -124,7 +124,7 @@ extern int dibusb2_0_power_ctrl(struct dvb_usb_device *, int); #define DEFAULT_RC_INTERVAL 150 //#define DEFAULT_RC_INTERVAL 100000 -extern struct dvb_usb_rc_key dibusb_rc_keys[]; +extern struct dvb_usb_rc_key ir_codes_dibusb_table[]; extern int dibusb_rc_query(struct dvb_usb_device *, u32 *, int *); extern int dibusb_read_eeprom_byte(struct dvb_usb_device *, u8, u8 *); diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index 955147d00756..e826077094fa 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -161,7 +161,7 @@ static int digitv_tuner_attach(struct dvb_usb_adapter *adap) return 0; } -static struct dvb_usb_rc_key digitv_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_digitv_table[] = { { 0x5f55, KEY_0 }, { 0x6f55, KEY_1 }, { 0x9f55, KEY_2 }, @@ -311,8 +311,8 @@ static struct dvb_usb_device_properties digitv_properties = { .identify_state = digitv_identify_state, .rc_interval = 1000, - .rc_key_map = digitv_rc_keys, - .rc_key_map_size = ARRAY_SIZE(digitv_rc_keys), + .rc_key_map = ir_codes_digitv_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_digitv_table), .rc_query = digitv_rc_query, .i2c_algo = &digitv_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index a1b12b01cbe4..f57e59044d4d 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -57,7 +57,7 @@ static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, /* remote control */ /* key list for the tiny remote control (Yakumo, don't know about the others) */ -static struct dvb_usb_rc_key dtt200u_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_dtt200u_table[] = { { 0x8001, KEY_MUTE }, { 0x8002, KEY_CHANNELDOWN }, { 0x8003, KEY_VOLUMEDOWN }, @@ -162,8 +162,8 @@ static struct dvb_usb_device_properties dtt200u_properties = { .power_ctrl = dtt200u_power_ctrl, .rc_interval = 300, - .rc_key_map = dtt200u_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys), + .rc_key_map = ir_codes_dtt200u_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), .rc_query = dtt200u_rc_query, .generic_bulk_ctrl_endpoint = 0x01, @@ -207,8 +207,8 @@ static struct dvb_usb_device_properties wt220u_properties = { .power_ctrl = dtt200u_power_ctrl, .rc_interval = 300, - .rc_key_map = dtt200u_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys), + .rc_key_map = ir_codes_dtt200u_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), .rc_query = dtt200u_rc_query, .generic_bulk_ctrl_endpoint = 0x01, @@ -252,8 +252,8 @@ static struct dvb_usb_device_properties wt220u_fc_properties = { .power_ctrl = dtt200u_power_ctrl, .rc_interval = 300, - .rc_key_map = dtt200u_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys), + .rc_key_map = ir_codes_dtt200u_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), .rc_query = dtt200u_rc_query, .generic_bulk_ctrl_endpoint = 0x01, @@ -297,8 +297,8 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = { .power_ctrl = dtt200u_power_ctrl, .rc_interval = 300, - .rc_key_map = dtt200u_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys), + .rc_key_map = ir_codes_dtt200u_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), .rc_query = dtt200u_rc_query, .generic_bulk_ctrl_endpoint = 0x01, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index ae8b57acfe05..085c4e457e0e 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -124,9 +124,11 @@ #define USB_PID_KWORLD_395U 0xe396 #define USB_PID_KWORLD_395U_2 0xe39b #define USB_PID_KWORLD_395U_3 0xe395 +#define USB_PID_KWORLD_395U_4 0xe39a #define USB_PID_KWORLD_MC810 0xc810 #define USB_PID_KWORLD_PC160_2T 0xc160 #define USB_PID_KWORLD_PC160_T 0xc161 +#define USB_PID_KWORLD_UB383_T 0xe383 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 @@ -288,6 +290,7 @@ #define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807 #define USB_PID_SONY_PLAYTV 0x0003 #define USB_PID_MYGICA_D689 0xd811 +#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011 #define USB_PID_ELGATO_EYETV_DTT 0x0021 #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 @@ -296,6 +299,8 @@ #define USB_PID_TVWAY_PLUS 0x0002 #define USB_PID_SVEON_STV20 0xe39d #define USB_PID_AZUREWAVE_AZ6027 0x3275 -#define USB_PID_TERRATEC_DVBS2CI 0x3275 -#define USB_PID_TECHNISAT_USB2_HDCI 0x0002 +#define USB_PID_TERRATEC_DVBS2CI_V1 0x10a4 +#define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac +#define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001 +#define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002 #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c index 6fe71c6745eb..bb46ba6a3573 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c @@ -42,6 +42,8 @@ int dvb_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, msleep(delay_ms); ret = usb_bulk_msg(d->udev,usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint_response ? + d->props.generic_bulk_ctrl_endpoint_response : d->props.generic_bulk_ctrl_endpoint),rbuf,rlen,&actlen, 2000); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 0143aef19ecd..4a9f676087bf 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -198,6 +198,12 @@ struct dvb_usb_adapter_properties { * is non-zero, one can use dvb_usb_generic_rw and dvb_usb_generic_write- * helper functions. * + * @generic_bulk_ctrl_endpoint_response: some DVB USB devices use a separate + * endpoint for responses to control messages sent with bulk transfers via + * the generic_bulk_ctrl_endpoint. When this is non-zero, this will be used + * instead of the generic_bulk_ctrl_endpoint when reading usb responses in + * the dvb_usb_generic_rw helper function. + * * @num_device_descs: number of struct dvb_usb_device_description in @devices * @devices: array of struct dvb_usb_device_description compatibles with these * properties. @@ -239,6 +245,7 @@ struct dvb_usb_device_properties { struct i2c_algorithm *i2c_algo; int generic_bulk_ctrl_endpoint; + int generic_bulk_ctrl_endpoint_response; int num_device_descs; struct dvb_usb_device_description devices[12]; diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index accc65509b07..e8fb85380672 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -73,7 +73,7 @@ "Please see linux/Documentation/dvb/ for more details " \ "on firmware-problems." -struct dvb_usb_rc_keys_table { +struct ir_codes_dvb_usb_table_table { struct dvb_usb_rc_key *rc_keys; int rc_keys_size; }; @@ -948,7 +948,7 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) return 0; } -static struct dvb_usb_rc_key dw210x_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_dw210x_table[] = { { 0xf80a, KEY_Q }, /*power*/ { 0xf80c, KEY_M }, /*mute*/ { 0xf811, KEY_1 }, @@ -982,7 +982,7 @@ static struct dvb_usb_rc_key dw210x_rc_keys[] = { { 0xf81b, KEY_B }, /*recall*/ }; -static struct dvb_usb_rc_key tevii_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_tevii_table[] = { { 0xf80a, KEY_POWER }, { 0xf80c, KEY_MUTE }, { 0xf811, KEY_1 }, @@ -1032,7 +1032,7 @@ static struct dvb_usb_rc_key tevii_rc_keys[] = { { 0xf858, KEY_SWITCHVIDEOMODE }, }; -static struct dvb_usb_rc_key tbs_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_tbs_table[] = { { 0xf884, KEY_POWER }, { 0xf894, KEY_MUTE }, { 0xf887, KEY_1 }, @@ -1067,10 +1067,10 @@ static struct dvb_usb_rc_key tbs_rc_keys[] = { { 0xf89b, KEY_MODE } }; -static struct dvb_usb_rc_keys_table keys_tables[] = { - { dw210x_rc_keys, ARRAY_SIZE(dw210x_rc_keys) }, - { tevii_rc_keys, ARRAY_SIZE(tevii_rc_keys) }, - { tbs_rc_keys, ARRAY_SIZE(tbs_rc_keys) }, +static struct ir_codes_dvb_usb_table_table keys_tables[] = { + { ir_codes_dw210x_table, ARRAY_SIZE(ir_codes_dw210x_table) }, + { ir_codes_tevii_table, ARRAY_SIZE(ir_codes_tevii_table) }, + { ir_codes_tbs_table, ARRAY_SIZE(ir_codes_tbs_table) }, }; static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) @@ -1185,14 +1185,14 @@ static int dw2102_load_firmware(struct usb_device *dev, /* init registers */ switch (dev->descriptor.idProduct) { case USB_PID_PROF_1100: - s6x0_properties.rc_key_map = tbs_rc_keys; + s6x0_properties.rc_key_map = ir_codes_tbs_table; s6x0_properties.rc_key_map_size = - ARRAY_SIZE(tbs_rc_keys); + ARRAY_SIZE(ir_codes_tbs_table); break; case USB_PID_TEVII_S650: - dw2104_properties.rc_key_map = tevii_rc_keys; + dw2104_properties.rc_key_map = ir_codes_tevii_table; dw2104_properties.rc_key_map_size = - ARRAY_SIZE(tevii_rc_keys); + ARRAY_SIZE(ir_codes_tevii_table); case USB_PID_DW2104: reset = 1; dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1, @@ -1255,8 +1255,8 @@ static struct dvb_usb_device_properties dw2102_properties = { .no_reconnect = 1, .i2c_algo = &dw2102_serit_i2c_algo, - .rc_key_map = dw210x_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys), + .rc_key_map = ir_codes_dw210x_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), .rc_interval = 150, .rc_query = dw2102_rc_query, @@ -1306,8 +1306,8 @@ static struct dvb_usb_device_properties dw2104_properties = { .no_reconnect = 1, .i2c_algo = &dw2104_i2c_algo, - .rc_key_map = dw210x_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys), + .rc_key_map = ir_codes_dw210x_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), .rc_interval = 150, .rc_query = dw2102_rc_query, @@ -1353,8 +1353,8 @@ static struct dvb_usb_device_properties dw3101_properties = { .no_reconnect = 1, .i2c_algo = &dw3101_i2c_algo, - .rc_key_map = dw210x_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys), + .rc_key_map = ir_codes_dw210x_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), .rc_interval = 150, .rc_query = dw2102_rc_query, @@ -1396,8 +1396,8 @@ static struct dvb_usb_device_properties s6x0_properties = { .no_reconnect = 1, .i2c_algo = &s6x0_i2c_algo, - .rc_key_map = tevii_rc_keys, - .rc_key_map_size = ARRAY_SIZE(tevii_rc_keys), + .rc_key_map = ir_codes_tevii_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_tevii_table), .rc_interval = 150, .rc_query = dw2102_rc_query, @@ -1459,8 +1459,8 @@ static int dw2102_probe(struct usb_interface *intf, /* fill only different fields */ p7500->firmware = "dvb-usb-p7500.fw"; p7500->devices[0] = d7500; - p7500->rc_key_map = tbs_rc_keys; - p7500->rc_key_map_size = ARRAY_SIZE(tbs_rc_keys); + p7500->rc_key_map = ir_codes_tbs_table; + p7500->rc_key_map_size = ARRAY_SIZE(ir_codes_tbs_table); p7500->adapter->frontend_attach = prof_7500_frontend_attach; if (0 == dvb_usb_device_init(intf, &dw2102_properties, diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c index d14bd227b502..93c21ddd0b77 100644 --- a/drivers/media/dvb/dvb-usb/friio-fe.c +++ b/drivers/media/dvb/dvb-usb/friio-fe.c @@ -300,7 +300,7 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { /** - * NOTE: ignore all the paramters except frequency. + * NOTE: ignore all the parameters except frequency. * others should be fixed to the proper value for ISDB-T, * but don't check here. */ diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c index afb444db43ad..45106ac49674 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk.c +++ b/drivers/media/dvb/dvb-usb/gp8psk.c @@ -105,6 +105,10 @@ static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d) ptr = fw->data; buf = kmalloc(64, GFP_KERNEL | GFP_DMA); + if (!buf) { + ret = -ENOMEM; + goto out_rel_fw; + } while (ptr[0] != 0xff) { u16 buflen = ptr[0] + 4; diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index 737ffa36ac9c..c211fef45fc3 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -589,7 +589,7 @@ static struct m920x_inits pinnacle310e_init[] = { }; /* ir keymaps */ -static struct dvb_usb_rc_key megasky_rc_keys [] = { +static struct dvb_usb_rc_key ir_codes_megasky_table [] = { { 0x0012, KEY_POWER }, { 0x001e, KEY_CYCLEWINDOWS }, /* min/max */ { 0x0002, KEY_CHANNELUP }, @@ -608,7 +608,7 @@ static struct dvb_usb_rc_key megasky_rc_keys [] = { { 0x000e, KEY_COFFEE }, /* "MTS" */ }; -static struct dvb_usb_rc_key tvwalkertwin_rc_keys [] = { +static struct dvb_usb_rc_key ir_codes_tvwalkertwin_table [] = { { 0x0001, KEY_ZOOM }, /* Full Screen */ { 0x0002, KEY_CAMERA }, /* snapshot */ { 0x0003, KEY_MUTE }, @@ -628,7 +628,7 @@ static struct dvb_usb_rc_key tvwalkertwin_rc_keys [] = { { 0x001e, KEY_VOLUMEUP }, }; -static struct dvb_usb_rc_key pinnacle310e_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_pinnacle310e_table[] = { { 0x16, KEY_POWER }, { 0x17, KEY_FAVORITES }, { 0x0f, KEY_TEXT }, @@ -785,8 +785,8 @@ static struct dvb_usb_device_properties megasky_properties = { .download_firmware = m920x_firmware_download, .rc_interval = 100, - .rc_key_map = megasky_rc_keys, - .rc_key_map_size = ARRAY_SIZE(megasky_rc_keys), + .rc_key_map = ir_codes_megasky_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_megasky_table), .rc_query = m920x_rc_query, .size_of_priv = sizeof(struct m920x_state), @@ -886,8 +886,8 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = { .download_firmware = m920x_firmware_download, .rc_interval = 100, - .rc_key_map = tvwalkertwin_rc_keys, - .rc_key_map_size = ARRAY_SIZE(tvwalkertwin_rc_keys), + .rc_key_map = ir_codes_tvwalkertwin_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_tvwalkertwin_table), .rc_query = m920x_rc_query, .size_of_priv = sizeof(struct m920x_state), @@ -993,8 +993,8 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { .download_firmware = NULL, .rc_interval = 100, - .rc_key_map = pinnacle310e_rc_keys, - .rc_key_map_size = ARRAY_SIZE(pinnacle310e_rc_keys), + .rc_key_map = ir_codes_pinnacle310e_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_pinnacle310e_table), .rc_query = m920x_rc_query, .size_of_priv = sizeof(struct m920x_state), diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c index b41d66ef8325..d195a587cc65 100644 --- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -21,7 +21,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define deb_ee(args...) dprintk(debug,0x02,args) /* Hauppauge NOVA-T USB2 keys */ -static struct dvb_usb_rc_key haupp_rc_keys [] = { +static struct dvb_usb_rc_key ir_codes_haupp_table [] = { { 0x1e00, KEY_0 }, { 0x1e01, KEY_1 }, { 0x1e02, KEY_2 }, @@ -91,14 +91,14 @@ static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state) deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",key[1],key[2],key[3],custom,data,toggle); - for (i = 0; i < ARRAY_SIZE(haupp_rc_keys); i++) { - if (rc5_data(&haupp_rc_keys[i]) == data && - rc5_custom(&haupp_rc_keys[i]) == custom) { + for (i = 0; i < ARRAY_SIZE(ir_codes_haupp_table); i++) { + if (rc5_data(&ir_codes_haupp_table[i]) == data && + rc5_custom(&ir_codes_haupp_table[i]) == custom) { - deb_rc("c: %x, d: %x\n", rc5_data(&haupp_rc_keys[i]), - rc5_custom(&haupp_rc_keys[i])); + deb_rc("c: %x, d: %x\n", rc5_data(&ir_codes_haupp_table[i]), + rc5_custom(&ir_codes_haupp_table[i])); - *event = haupp_rc_keys[i].event; + *event = ir_codes_haupp_table[i].event; *state = REMOTE_KEY_PRESSED; if (st->old_toggle == toggle) { if (st->last_repeat_count++ < 2) @@ -196,8 +196,8 @@ static struct dvb_usb_device_properties nova_t_properties = { .read_mac_address = nova_t_read_mac_address, .rc_interval = 100, - .rc_key_map = haupp_rc_keys, - .rc_key_map_size = ARRAY_SIZE(haupp_rc_keys), + .rc_key_map = ir_codes_haupp_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_haupp_table), .rc_query = nova_t_rc_query, .i2c_algo = &dibusb_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c index 830557696ae6..dfb81ff1d9a7 100644 --- a/drivers/media/dvb/dvb-usb/opera1.c +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -35,7 +35,7 @@ struct opera1_state { u32 last_key_pressed; }; -struct opera_rc_keys { +struct ir_codes_opera_table { u32 keycode; u32 event; }; @@ -331,7 +331,7 @@ static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) return 0; } -static struct dvb_usb_rc_key opera1_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_opera1_table[] = { {0x5fa0, KEY_1}, {0x51af, KEY_2}, {0x5da2, KEY_3}, @@ -404,12 +404,12 @@ static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) send_key = (send_key & 0xffff) | 0x0100; - for (i = 0; i < ARRAY_SIZE(opera1_rc_keys); i++) { - if (rc5_scan(&opera1_rc_keys[i]) == (send_key & 0xffff)) { + for (i = 0; i < ARRAY_SIZE(ir_codes_opera1_table); i++) { + if (rc5_scan(&ir_codes_opera1_table[i]) == (send_key & 0xffff)) { *state = REMOTE_KEY_PRESSED; - *event = opera1_rc_keys[i].event; + *event = ir_codes_opera1_table[i].event; opst->last_key_pressed = - opera1_rc_keys[i].event; + ir_codes_opera1_table[i].event; break; } opst->last_key_pressed = 0; @@ -498,8 +498,8 @@ static struct dvb_usb_device_properties opera1_properties = { .power_ctrl = opera1_power_ctrl, .i2c_algo = &opera1_i2c_algo, - .rc_key_map = opera1_rc_keys, - .rc_key_map_size = ARRAY_SIZE(opera1_rc_keys), + .rc_key_map = ir_codes_opera1_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_opera1_table), .rc_interval = 200, .rc_query = opera1_rc_query, .read_mac_address = opera1_read_mac_address, diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c index ef4e37d9c5ff..4d332451653b 100644 --- a/drivers/media/dvb/dvb-usb/vp702x.c +++ b/drivers/media/dvb/dvb-usb/vp702x.c @@ -174,7 +174,7 @@ static int vp702x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } /* keys for the enclosed remote control */ -static struct dvb_usb_rc_key vp702x_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_vp702x_table[] = { { 0x0001, KEY_1 }, { 0x0002, KEY_2 }, }; @@ -197,10 +197,10 @@ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return 0; } - for (i = 0; i < ARRAY_SIZE(vp702x_rc_keys); i++) - if (rc5_custom(&vp702x_rc_keys[i]) == key[1]) { + for (i = 0; i < ARRAY_SIZE(ir_codes_vp702x_table); i++) + if (rc5_custom(&ir_codes_vp702x_table[i]) == key[1]) { *state = REMOTE_KEY_PRESSED; - *event = vp702x_rc_keys[i].event; + *event = ir_codes_vp702x_table[i].event; break; } return 0; @@ -283,8 +283,8 @@ static struct dvb_usb_device_properties vp702x_properties = { }, .read_mac_address = vp702x_read_mac_addr, - .rc_key_map = vp702x_rc_keys, - .rc_key_map_size = ARRAY_SIZE(vp702x_rc_keys), + .rc_key_map = ir_codes_vp702x_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_vp702x_table), .rc_interval = 400, .rc_query = vp702x_rc_query, diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c index a59faa27912a..036893fa4480 100644 --- a/drivers/media/dvb/dvb-usb/vp7045.c +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -99,7 +99,7 @@ static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) /* The keymapping struct. Somehow this should be loaded to the driver, but * currently it is hardcoded. */ -static struct dvb_usb_rc_key vp7045_rc_keys[] = { +static struct dvb_usb_rc_key ir_codes_vp7045_table[] = { { 0x0016, KEY_POWER }, { 0x0010, KEY_MUTE }, { 0x0003, KEY_1 }, @@ -165,10 +165,10 @@ static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return 0; } - for (i = 0; i < ARRAY_SIZE(vp7045_rc_keys); i++) - if (rc5_data(&vp7045_rc_keys[i]) == key) { + for (i = 0; i < ARRAY_SIZE(ir_codes_vp7045_table); i++) + if (rc5_data(&ir_codes_vp7045_table[i]) == key) { *state = REMOTE_KEY_PRESSED; - *event = vp7045_rc_keys[i].event; + *event = ir_codes_vp7045_table[i].event; break; } return 0; @@ -260,8 +260,8 @@ static struct dvb_usb_device_properties vp7045_properties = { .read_mac_address = vp7045_read_mac_addr, .rc_interval = 400, - .rc_key_map = vp7045_rc_keys, - .rc_key_map_size = ARRAY_SIZE(vp7045_rc_keys), + .rc_key_map = ir_codes_vp7045_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_vp7045_table), .rc_query = vp7045_rc_query, .num_device_descs = 2, diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index 1b31bebc27d6..28294af752db 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -1096,7 +1096,7 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) c->operand[15] = msg[1]; /* Program number */ c->operand[16] = msg[2]; - c->operand[17] = 0x01; /* Version number=0 + current/next=1 */ + c->operand[17] = msg[3]; /* Version number and current/next */ c->operand[18] = 0x00; /* Section number=0 */ c->operand[19] = 0x00; /* Last section number=0 */ c->operand[20] = 0x1f; /* PCR_PID=1FFF */ diff --git a/drivers/media/dvb/frontends/atbm8830_priv.h b/drivers/media/dvb/frontends/atbm8830_priv.h index ce960f76092a..d460058d497e 100644 --- a/drivers/media/dvb/frontends/atbm8830_priv.h +++ b/drivers/media/dvb/frontends/atbm8830_priv.h @@ -18,7 +18,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - + #ifndef __ATBM8830_PRIV_H #define __ATBM8830_PRIV_H diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c index 24268ef2753d..68dba3a4b4da 100644 --- a/drivers/media/dvb/frontends/au8522_decoder.c +++ b/drivers/media/dvb/frontends/au8522_decoder.c @@ -664,6 +664,13 @@ static int au8522_reset(struct v4l2_subdev *sd, u32 val) { struct au8522_state *state = to_state(sd); + state->operational_mode = AU8522_ANALOG_MODE; + + /* Clear out any state associated with the digital side of the + chip, so that when it gets powered back up it won't think + that it is already tuned */ + state->current_frequency = 0; + au8522_writereg(state, 0xa4, 1 << 5); return 0; diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c index a1fed0fa8ed4..65f6a36dfb21 100644 --- a/drivers/media/dvb/frontends/au8522_dig.c +++ b/drivers/media/dvb/frontends/au8522_dig.c @@ -84,6 +84,14 @@ static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) dprintk("%s(%d)\n", __func__, enable); + if (state->operational_mode == AU8522_ANALOG_MODE) { + /* We're being asked to manage the gate even though we're + not in digital mode. This can occur if we get switched + over to analog mode before the dvb_frontend kernel thread + has completely shutdown */ + return 0; + } + if (enable) return au8522_writereg(state, 0x106, 1); else @@ -608,6 +616,13 @@ int au8522_init(struct dvb_frontend *fe) struct au8522_state *state = fe->demodulator_priv; dprintk("%s()\n", __func__); + state->operational_mode = AU8522_DIGITAL_MODE; + + /* Clear out any state associated with the digital side of the + chip, so that when it gets powered back up it won't think + that it is already tuned */ + state->current_frequency = 0; + au8522_writereg(state, 0xa4, 1 << 5); au8522_i2c_gate_ctrl(fe, 1); @@ -704,6 +719,15 @@ int au8522_sleep(struct dvb_frontend *fe) struct au8522_state *state = fe->demodulator_priv; dprintk("%s()\n", __func__); + /* Only power down if the digital side is currently using the chip */ + if (state->operational_mode == AU8522_ANALOG_MODE) { + /* We're not in one of the expected power modes, which means + that the DVB thread is probably telling us to go to sleep + even though the analog frontend has already started using + the chip. So ignore the request */ + return 0; + } + /* turn off led */ au8522_led_ctrl(state, 0); @@ -932,6 +956,8 @@ struct dvb_frontend *au8522_attach(const struct au8522_config *config, /* setup the state */ state->config = config; state->i2c = i2c; + state->operational_mode = AU8522_DIGITAL_MODE; + /* create dvb_frontend */ memcpy(&state->frontend.ops, &au8522_ops, sizeof(struct dvb_frontend_ops)); diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h index c74c4e72fe91..609cf04bc312 100644 --- a/drivers/media/dvb/frontends/au8522_priv.h +++ b/drivers/media/dvb/frontends/au8522_priv.h @@ -34,10 +34,15 @@ #include "au8522.h" #include "tuner-i2c.h" +#define AU8522_ANALOG_MODE 0 +#define AU8522_DIGITAL_MODE 1 + struct au8522_state { struct i2c_client *c; struct i2c_adapter *i2c; + u8 operational_mode; + /* Used for sharing of the state between analog and digital mode */ struct tuner_i2c_props i2c_props; struct list_head hybrid_tuner_instance_list; diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c index 40a099810279..afad252abf41 100644 --- a/drivers/media/dvb/frontends/dib3000mc.c +++ b/drivers/media/dvb/frontends/dib3000mc.c @@ -814,42 +814,51 @@ EXPORT_SYMBOL(dib3000mc_set_config); int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[]) { - struct dib3000mc_state st = { .i2c_adap = i2c }; + struct dib3000mc_state *dmcst; int k; u8 new_addr; static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26}; + dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); + if (dmcst == NULL) + return -ENODEV; + + dmcst->i2c_adap = i2c; + for (k = no_of_demods-1; k >= 0; k--) { - st.cfg = &cfg[k]; + dmcst->cfg = &cfg[k]; /* designated i2c address */ new_addr = DIB3000MC_I2C_ADDRESS[k]; - st.i2c_addr = new_addr; - if (dib3000mc_identify(&st) != 0) { - st.i2c_addr = default_addr; - if (dib3000mc_identify(&st) != 0) { + dmcst->i2c_addr = new_addr; + if (dib3000mc_identify(dmcst) != 0) { + dmcst->i2c_addr = default_addr; + if (dib3000mc_identify(dmcst) != 0) { dprintk("-E- DiB3000P/MC #%d: not identified\n", k); + kfree(dmcst); return -ENODEV; } } - dib3000mc_set_output_mode(&st, OUTMODE_MPEG2_PAR_CONT_CLK); + dib3000mc_set_output_mode(dmcst, OUTMODE_MPEG2_PAR_CONT_CLK); // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0) - dib3000mc_write_word(&st, 1024, (new_addr << 3) | 0x1); - st.i2c_addr = new_addr; + dib3000mc_write_word(dmcst, 1024, (new_addr << 3) | 0x1); + dmcst->i2c_addr = new_addr; } for (k = 0; k < no_of_demods; k++) { - st.cfg = &cfg[k]; - st.i2c_addr = DIB3000MC_I2C_ADDRESS[k]; + dmcst->cfg = &cfg[k]; + dmcst->i2c_addr = DIB3000MC_I2C_ADDRESS[k]; - dib3000mc_write_word(&st, 1024, st.i2c_addr << 3); + dib3000mc_write_word(dmcst, 1024, dmcst->i2c_addr << 3); /* turn off data output */ - dib3000mc_set_output_mode(&st, OUTMODE_HIGH_Z); + dib3000mc_set_output_mode(dmcst, OUTMODE_HIGH_Z); } + + kfree(dmcst); return 0; } EXPORT_SYMBOL(dib3000mc_i2c_enumeration); diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 85468a45c344..2e28b973dfd3 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -1324,46 +1324,54 @@ EXPORT_SYMBOL(dib7000p_pid_filter); int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) { - struct dib7000p_state st = { .i2c_adap = i2c }; + struct dib7000p_state *dpst; int k = 0; u8 new_addr = 0; + dpst = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); + if (!dpst) + return -ENOMEM; + + dpst->i2c_adap = i2c; + for (k = no_of_demods-1; k >= 0; k--) { - st.cfg = cfg[k]; + dpst->cfg = cfg[k]; /* designated i2c address */ new_addr = (0x40 + k) << 1; - st.i2c_addr = new_addr; - dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */ - if (dib7000p_identify(&st) != 0) { - st.i2c_addr = default_addr; - dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */ - if (dib7000p_identify(&st) != 0) { + dpst->i2c_addr = new_addr; + dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ + if (dib7000p_identify(dpst) != 0) { + dpst->i2c_addr = default_addr; + dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ + if (dib7000p_identify(dpst) != 0) { dprintk("DiB7000P #%d: not identified\n", k); + kfree(dpst); return -EIO; } } /* start diversity to pull_down div_str - just for i2c-enumeration */ - dib7000p_set_output_mode(&st, OUTMODE_DIVERSITY); + dib7000p_set_output_mode(dpst, OUTMODE_DIVERSITY); /* set new i2c address and force divstart */ - dib7000p_write_word(&st, 1285, (new_addr << 2) | 0x2); + dib7000p_write_word(dpst, 1285, (new_addr << 2) | 0x2); dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); } for (k = 0; k < no_of_demods; k++) { - st.cfg = cfg[k]; - st.i2c_addr = (0x40 + k) << 1; + dpst->cfg = cfg[k]; + dpst->i2c_addr = (0x40 + k) << 1; // unforce divstr - dib7000p_write_word(&st, 1285, st.i2c_addr << 2); + dib7000p_write_word(dpst, 1285, dpst->i2c_addr << 2); /* deactivate div - it was just for i2c-enumeration */ - dib7000p_set_output_mode(&st, OUTMODE_HIGH_Z); + dib7000p_set_output_mode(dpst, OUTMODE_HIGH_Z); } + kfree(dpst); return 0; } EXPORT_SYMBOL(dib7000p_i2c_enumeration); diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h index b1ee20799639..e0a9ded11df4 100644 --- a/drivers/media/dvb/frontends/dib8000.h +++ b/drivers/media/dvb/frontends/dib8000.h @@ -109,6 +109,7 @@ static inline void dib8000_pwm_agc_reset(struct dvb_frontend *fe) static inline s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return 0; } #endif diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index cff3535566fe..78001e8bcdb7 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -719,7 +719,7 @@ static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) (ds3000_readreg(state, 0x8d) << 4); dvbs2_signal_reading = ds3000_readreg(state, 0x8e); tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1; - if (dvbs2_signal_reading == 0) { + if (tmp == 0) { *snr = 0x0000; return 0; } diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 01f8f1f802fd..4f5e7d3a0e61 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -1583,7 +1583,7 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe, struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct stv0900_search_params p_search; - struct stv0900_signal_info p_result; + struct stv0900_signal_info p_result = intp->result[demod]; enum fe_stv0900_error error = STV0900_NO_ERROR; @@ -1842,6 +1842,19 @@ static void stv0900_release(struct dvb_frontend *fe) kfree(state); } +static int stv0900_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + struct stv0900_signal_info p_result = intp->result[demod]; + + p->frequency = p_result.locked ? p_result.frequency : 0; + p->u.qpsk.symbol_rate = p_result.locked ? p_result.symbol_rate : 0; + return 0; +} + static struct dvb_frontend_ops stv0900_ops = { .info = { @@ -1862,6 +1875,7 @@ static struct dvb_frontend_ops stv0900_ops = { }, .release = stv0900_release, .init = stv0900_init, + .get_frontend = stv0900_get_frontend, .get_frontend_algo = stv0900_frontend_algo, .i2c_gate_ctrl = stv0900_i2c_gate_ctrl, .diseqc_send_master_cmd = stv0900_send_master_cmd, diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 96972804f4ad..425e7a43ae19 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -754,11 +754,19 @@ static int stv090x_write_reg(struct stv090x_state *state, unsigned int reg, u8 d return stv090x_write_regs(state, reg, &data, 1); } -static int stv090x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) { - struct stv090x_state *state = fe->demodulator_priv; u32 reg; + /* + * NOTE! A lock is used as a FSM to control the state in which + * access is serialized between two tuners on the same demod. + * This has nothing to do with a lock to protect a critical section + * which may in some other cases be confused with protecting I/O + * access to the demodulator gate. + * In case of any error, the lock is unlocked and exit within the + * relevant operations themselves. + */ if (enable) mutex_lock(&state->internal->tuner_lock); @@ -1778,7 +1786,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) freq -= cur_step * car_step; /* Setup tuner */ - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_set_frequency) { @@ -1791,12 +1799,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) goto err_gateoff; } - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; msleep(50); - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_get_status) { @@ -1809,7 +1817,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) else dprintk(FE_DEBUG, 1, "Tuner unlocked"); - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; } @@ -1822,7 +1830,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) return srate_coarse; err_gateoff: - stv090x_i2c_gate_ctrl(fe, 0); + stv090x_i2c_gate_ctrl(state, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -2167,7 +2175,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) freq -= cur_step * car_step; /* Setup tuner */ - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_set_frequency) { @@ -2180,12 +2188,12 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) goto err_gateoff; } - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; msleep(50); - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_get_status) { @@ -2198,7 +2206,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) else dprintk(FE_DEBUG, 1, "Tuner unlocked"); - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c); @@ -2222,7 +2230,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) return lock; err_gateoff: - stv090x_i2c_gate_ctrl(fe, 0); + stv090x_i2c_gate_ctrl(state, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -2591,7 +2599,7 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st } state->delsys = stv090x_get_std(state); - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_get_frequency) { @@ -2599,7 +2607,7 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st goto err_gateoff; } - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; offst_freq = stv090x_get_car_freq(state, state->internal->mclk) / 1000; @@ -2619,7 +2627,7 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) { - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_get_frequency) { @@ -2627,7 +2635,7 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st goto err_gateoff; } - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; if (abs(offst_freq) <= ((state->search_range / 2000) + 500)) @@ -2646,7 +2654,7 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st return STV090x_OUTOFRANGE; err_gateoff: - stv090x_i2c_gate_ctrl(fe, 0); + stv090x_i2c_gate_ctrl(state, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -3000,7 +3008,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) if (state->algo != STV090x_WARM_SEARCH) { - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_set_bandwidth) { @@ -3008,7 +3016,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) goto err_gateoff; } - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; } @@ -3059,7 +3067,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) return 0; err_gateoff: - stv090x_i2c_gate_ctrl(fe, 0); + stv090x_i2c_gate_ctrl(state, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -3235,7 +3243,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) } /* Setup tuner */ - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_set_bbgain) { @@ -3256,17 +3264,17 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) goto err_gateoff; } - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; msleep(50); if (state->config->tuner_get_status) { - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (state->config->tuner_get_status(fe, ®) < 0) goto err_gateoff; - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; if (reg) @@ -3400,7 +3408,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) return signal_state; err_gateoff: - stv090x_i2c_gate_ctrl(fe, 0); + stv090x_i2c_gate_ctrl(state, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -3839,6 +3847,17 @@ static int stv090x_sleep(struct dvb_frontend *fe) struct stv090x_state *state = fe->demodulator_priv; u32 reg; + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (state->config->tuner_sleep) { + if (state->config->tuner_sleep(fe) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + dprintk(FE_DEBUG, 1, "Set %s to sleep", state->device == STV0900 ? "STV0900" : "STV0903"); @@ -3853,6 +3872,9 @@ static int stv090x_sleep(struct dvb_frontend *fe) goto err; return 0; + +err_gateoff: + stv090x_i2c_gate_ctrl(state, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -4311,6 +4333,20 @@ static int stv090x_init(struct dvb_frontend *fe) u32 reg; if (state->internal->mclk == 0) { + /* call tuner init to configure the tuner's clock output + divider directly before setting up the master clock of + the stv090x. */ + if (stv090x_i2c_gate_ctrl(state, 1) < 0) + goto err; + + if (config->tuner_init) { + if (config->tuner_init(fe) < 0) + goto err_gateoff; + } + + if (stv090x_i2c_gate_ctrl(state, 0) < 0) + goto err; + stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */ msleep(5); if (stv090x_write_reg(state, STV090x_SYNTCTRL, @@ -4336,7 +4372,7 @@ static int stv090x_init(struct dvb_frontend *fe) if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) goto err; - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; if (config->tuner_set_mode) { @@ -4349,7 +4385,7 @@ static int stv090x_init(struct dvb_frontend *fe) goto err_gateoff; } - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) + if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; if (stv090x_set_tspath(state) < 0) @@ -4358,7 +4394,7 @@ static int stv090x_init(struct dvb_frontend *fe) return 0; err_gateoff: - stv090x_i2c_gate_ctrl(fe, 0); + stv090x_i2c_gate_ctrl(state, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -4503,8 +4539,6 @@ static struct dvb_frontend_ops stv090x_ops = { .sleep = stv090x_sleep, .get_frontend_algo = stv090x_frontend_algo, - .i2c_gate_ctrl = stv090x_i2c_gate_ctrl, - .diseqc_send_master_cmd = stv090x_send_diseqc_msg, .diseqc_send_burst = stv090x_send_diseqc_burst, .diseqc_recv_slave_reply = stv090x_recv_slave_reply, diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h index 30f01a6902ac..dd1b93ae4e9d 100644 --- a/drivers/media/dvb/frontends/stv090x.h +++ b/drivers/media/dvb/frontends/stv090x.h @@ -87,6 +87,7 @@ struct stv090x_config { bool diseqc_envelope_mode; int (*tuner_init) (struct dvb_frontend *fe); + int (*tuner_sleep) (struct dvb_frontend *fe); int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode); int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency); int (*tuner_get_frequency) (struct dvb_frontend *fe, u32 *frequency); diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c index dea4245f077c..42591ce1aaad 100644 --- a/drivers/media/dvb/frontends/stv6110x.c +++ b/drivers/media/dvb/frontends/stv6110x.c @@ -338,14 +338,12 @@ static struct dvb_tuner_ops stv6110x_ops = { .frequency_max = 2150000, .frequency_step = 0, }, - - .init = stv6110x_init, - .sleep = stv6110x_sleep, .release = stv6110x_release }; static struct stv6110x_devctl stv6110x_ctl = { .tuner_init = stv6110x_init, + .tuner_sleep = stv6110x_sleep, .tuner_set_mode = stv6110x_set_mode, .tuner_set_frequency = stv6110x_set_frequency, .tuner_get_frequency = stv6110x_get_frequency, @@ -363,11 +361,10 @@ struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, { struct stv6110x_state *stv6110x; u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; - int ret; stv6110x = kzalloc(sizeof (struct stv6110x_state), GFP_KERNEL); - if (stv6110x == NULL) - goto error; + if (!stv6110x) + return NULL; stv6110x->i2c = i2c; stv6110x->config = config; @@ -392,34 +389,11 @@ struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, break; } - if (fe->ops.i2c_gate_ctrl) { - ret = fe->ops.i2c_gate_ctrl(fe, 1); - if (ret < 0) - goto error; - } - - ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs, - ARRAY_SIZE(stv6110x->regs)); - if (ret < 0) { - dprintk(FE_ERROR, 1, "Initialization failed"); - goto error; - } - - if (fe->ops.i2c_gate_ctrl) { - ret = fe->ops.i2c_gate_ctrl(fe, 0); - if (ret < 0) - goto error; - } - fe->tuner_priv = stv6110x; fe->ops.tuner_ops = stv6110x_ops; - printk("%s: Attaching STV6110x \n", __func__); + printk(KERN_INFO "%s: Attaching STV6110x\n", __func__); return stv6110x->devctl; - -error: - kfree(stv6110x); - return NULL; } EXPORT_SYMBOL(stv6110x_attach); diff --git a/drivers/media/dvb/frontends/stv6110x.h b/drivers/media/dvb/frontends/stv6110x.h index 2429ae6d7847..47516753929a 100644 --- a/drivers/media/dvb/frontends/stv6110x.h +++ b/drivers/media/dvb/frontends/stv6110x.h @@ -40,6 +40,7 @@ enum tuner_status { struct stv6110x_devctl { int (*tuner_init) (struct dvb_frontend *fe); + int (*tuner_sleep) (struct dvb_frontend *fe); int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode); int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency); int (*tuner_get_frequency) (struct dvb_frontend *fe, u32 *frequency); diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c index 4675a3b53c7d..3d4e4663220c 100644 --- a/drivers/media/dvb/mantis/mantis_input.c +++ b/drivers/media/dvb/mantis/mantis_input.c @@ -32,6 +32,8 @@ #include "mantis_reg.h" #include "mantis_uart.h" +#define MODULE_NAME "mantis_core" + static struct ir_scancode mantis_ir_table[] = { { 0x29, KEY_POWER }, { 0x28, KEY_FAVORITES }, @@ -126,7 +128,7 @@ int mantis_input_init(struct mantis_pci *mantis) rc->id.version = 1; rc->dev = mantis->pdev->dev; - err = ir_input_register(rc, &ir_mantis, NULL); + err = __ir_input_register(rc, &ir_mantis, NULL, MODULE_NAME); if (err) { dprintk(MANTIS_ERROR, 1, "IR device registration failed, ret = %d", err); input_free_device(rc); diff --git a/drivers/media/dvb/mantis/mantis_vp1041.c b/drivers/media/dvb/mantis/mantis_vp1041.c index 515346dd31d0..d1aa2bc0c155 100644 --- a/drivers/media/dvb/mantis/mantis_vp1041.c +++ b/drivers/media/dvb/mantis/mantis_vp1041.c @@ -136,12 +136,12 @@ static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { { STB0899_RCOMPC , 0xc9 }, { STB0899_AGC1CN , 0x01 }, { STB0899_AGC1REF , 0x10 }, - { STB0899_RTC , 0x23 }, + { STB0899_RTC , 0x23 }, { STB0899_TMGCFG , 0x4e }, { STB0899_AGC2REF , 0x34 }, { STB0899_TLSR , 0x84 }, { STB0899_CFD , 0xf7 }, - { STB0899_ACLC , 0x87 }, + { STB0899_ACLC , 0x87 }, { STB0899_BCLC , 0x94 }, { STB0899_EQON , 0x41 }, { STB0899_LDT , 0xf1 }, @@ -194,10 +194,10 @@ static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { { STB0899_ECNT3M , 0x0a }, { STB0899_ECNT3L , 0xad }, { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, + { STB0899_FECM , 0x01 }, { STB0899_VTH12 , 0xb0 }, { STB0899_VTH23 , 0x7a }, - { STB0899_VTH34 , 0x58 }, + { STB0899_VTH34 , 0x58 }, { STB0899_VTH56 , 0x38 }, { STB0899_VTH67 , 0x34 }, { STB0899_VTH78 , 0x24 }, @@ -206,7 +206,7 @@ static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ { STB0899_TSULC , 0x42 }, { STB0899_RSLLC , 0x41 }, - { STB0899_TSLPL , 0x12 }, + { STB0899_TSLPL , 0x12 }, { STB0899_TSCFGH , 0x0c }, { STB0899_TSCFGM , 0x00 }, { STB0899_TSCFGL , 0x00 }, diff --git a/drivers/media/dvb/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig index 3ec8e6fcbb1d..cec242b7c00d 100644 --- a/drivers/media/dvb/ngene/Kconfig +++ b/drivers/media/dvb/ngene/Kconfig @@ -4,6 +4,8 @@ config DVB_NGENE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_STV6110x if !DVB_FE_CUSTOMISE select DVB_STV090x if !DVB_FE_CUSTOMISE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE ---help--- Support for Micronas PCI express cards with nGene bridge. diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile index 40435cad4819..0608aabb14ee 100644 --- a/drivers/media/dvb/ngene/Makefile +++ b/drivers/media/dvb/ngene/Makefile @@ -2,10 +2,10 @@ # Makefile for the nGene device driver # -ngene-objs := ngene-core.o +ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o obj-$(CONFIG_DVB_NGENE) += ngene.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/ - +EXTRA_CFLAGS += -Idrivers/media/common/tuners/ diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c new file mode 100644 index 000000000000..692c3e226e83 --- /dev/null +++ b/drivers/media/dvb/ngene/ngene-cards.c @@ -0,0 +1,328 @@ +/* + * ngene-cards.c: nGene PCIe bridge driver - card specific info + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de> + * Modifications for new nGene firmware, + * support for EEPROM-copying, + * support for new dual DVB-S2 card prototype + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#include "ngene.h" + +/* demods/tuners */ +#include "stv6110x.h" +#include "stv090x.h" +#include "lnbh24.h" +#include "lgdt330x.h" +#include "mt2131.h" + + +/****************************************************************************/ +/* Demod/tuner attachment ***************************************************/ +/****************************************************************************/ + +static int tuner_attach_stv6110(struct ngene_channel *chan) +{ + struct stv090x_config *feconf = (struct stv090x_config *) + chan->dev->card_info->fe_config[chan->number]; + struct stv6110x_config *tunerconf = (struct stv6110x_config *) + chan->dev->card_info->tuner_config[chan->number]; + struct stv6110x_devctl *ctl; + + ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, + &chan->i2c_adapter); + if (ctl == NULL) { + printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); + return -ENODEV; + } + + feconf->tuner_init = ctl->tuner_init; + feconf->tuner_set_mode = ctl->tuner_set_mode; + feconf->tuner_set_frequency = ctl->tuner_set_frequency; + feconf->tuner_get_frequency = ctl->tuner_get_frequency; + feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; + feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; + feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; + feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; + feconf->tuner_set_refclk = ctl->tuner_set_refclk; + feconf->tuner_get_status = ctl->tuner_get_status; + + return 0; +} + + +static int demod_attach_stv0900(struct ngene_channel *chan) +{ + struct stv090x_config *feconf = (struct stv090x_config *) + chan->dev->card_info->fe_config[chan->number]; + + chan->fe = dvb_attach(stv090x_attach, + feconf, + &chan->i2c_adapter, + chan->number == 0 ? STV090x_DEMODULATOR_0 : + STV090x_DEMODULATOR_1); + if (chan->fe == NULL) { + printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); + return -ENODEV; + } + + if (!dvb_attach(lnbh24_attach, chan->fe, &chan->i2c_adapter, 0, + 0, chan->dev->card_info->lnb[chan->number])) { + printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); + dvb_frontend_detach(chan->fe); + return -ENODEV; + } + + return 0; +} + +static struct lgdt330x_config aver_m780 = { + .demod_address = 0xb2 >> 1, + .demod_chip = LGDT3303, + .serial_mpeg = 0x00, /* PARALLEL */ + .clock_polarity_flip = 1, +}; + +static struct mt2131_config m780_tunerconfig = { + 0xc0 >> 1 +}; + +/* A single func to attach the demo and tuner, rather than + * use two sep funcs like the current design mandates. + */ +static int demod_attach_lg330x(struct ngene_channel *chan) +{ + chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter); + if (chan->fe == NULL) { + printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n"); + return -ENODEV; + } + + dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter, + &m780_tunerconfig, 0); + + return (chan->fe) ? 0 : -ENODEV; +} + +/****************************************************************************/ +/* Switch control (I2C gates, etc.) *****************************************/ +/****************************************************************************/ + + +static struct stv090x_config fe_cineS2 = { + .device = STV0900, + .demod_mode = STV090x_DUAL, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 27000000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + + .repeater_level = STV090x_RPTLEVEL_16, + + .adc1_range = STV090x_ADC_1Vpp, + .adc2_range = STV090x_ADC_1Vpp, + + .diseqc_envelope_mode = true, +}; + +static struct stv6110x_config tuner_cineS2_0 = { + .addr = 0x60, + .refclk = 27000000, + .clk_div = 1, +}; + +static struct stv6110x_config tuner_cineS2_1 = { + .addr = 0x63, + .refclk = 27000000, + .clk_div = 1, +}; + +static struct ngene_info ngene_info_cineS2 = { + .type = NGENE_SIDEWINDER, + .name = "Linux4Media cineS2 DVB-S2 Twin Tuner", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 15, +}; + +static struct ngene_info ngene_info_satixS2 = { + .type = NGENE_SIDEWINDER, + .name = "Mystique SaTiX-S2 Dual", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 15, +}; + +static struct ngene_info ngene_info_satixS2v2 = { + .type = NGENE_SIDEWINDER, + .name = "Mystique SaTiX-S2 Dual (v2)", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0a, 0x08}, + .tsf = {3, 3}, + .fw_version = 15, +}; + +static struct ngene_info ngene_info_cineS2v5 = { + .type = NGENE_SIDEWINDER, + .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0a, 0x08}, + .tsf = {3, 3}, + .fw_version = 15, +}; + +static struct ngene_info ngene_info_m780 = { + .type = NGENE_APP, + .name = "Aver M780 ATSC/QAM-B", + + /* Channel 0 is analog, which is currently unsupported */ + .io_type = { NGENE_IO_NONE, NGENE_IO_TSIN }, + .demod_attach = { NULL, demod_attach_lg330x }, + + /* Ensure these are NULL else the frame will call them (as funcs) */ + .tuner_attach = { 0, 0, 0, 0 }, + .fe_config = { NULL, &aver_m780 }, + .avf = { 0 }, + + /* A custom electrical interface config for the demod to bridge */ + .tsf = { 4, 4 }, + .fw_version = 15, +}; + +/****************************************************************************/ + + + +/****************************************************************************/ +/* PCI Subsystem ID *********************************************************/ +/****************************************************************************/ + +#define NGENE_ID(_subvend, _subdev, _driverdata) { \ + .vendor = NGENE_VID, .device = NGENE_PID, \ + .subvendor = _subvend, .subdevice = _subdev, \ + .driver_data = (unsigned long) &_driverdata } + +/****************************************************************************/ + +static const struct pci_device_id ngene_id_tbl[] __devinitdata = { + NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), + NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), + NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2), + NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2), + NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5), + NGENE_ID(0x1461, 0x062e, ngene_info_m780), + {0} +}; +MODULE_DEVICE_TABLE(pci, ngene_id_tbl); + +/****************************************************************************/ +/* Init/Exit ****************************************************************/ +/****************************************************************************/ + +static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, + enum pci_channel_state state) +{ + printk(KERN_ERR DEVICE_NAME ": PCI error\n"); + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + if (state == pci_channel_io_frozen) + return PCI_ERS_RESULT_NEED_RESET; + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static pci_ers_result_t ngene_link_reset(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": link reset\n"); + return 0; +} + +static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": slot reset\n"); + return 0; +} + +static void ngene_resume(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": resume\n"); +} + +static struct pci_error_handlers ngene_errors = { + .error_detected = ngene_error_detected, + .link_reset = ngene_link_reset, + .slot_reset = ngene_slot_reset, + .resume = ngene_resume, +}; + +static struct pci_driver ngene_pci_driver = { + .name = "ngene", + .id_table = ngene_id_tbl, + .probe = ngene_probe, + .remove = __devexit_p(ngene_remove), + .err_handler = &ngene_errors, +}; + +static __init int module_init_ngene(void) +{ + printk(KERN_INFO + "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); + return pci_register_driver(&ngene_pci_driver); +} + +static __exit void module_exit_ngene(void) +{ + pci_unregister_driver(&ngene_pci_driver); +} + +module_init(module_init_ngene); +module_exit(module_exit_ngene); + +MODULE_DESCRIPTION("nGene"); +MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 645e8b8a7137..c8b4dfa0ab5f 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -34,20 +34,14 @@ #include <linux/io.h> #include <asm/div64.h> #include <linux/pci.h> -#include <linux/pci_ids.h> #include <linux/smp_lock.h> #include <linux/timer.h> -#include <linux/version.h> #include <linux/byteorder/generic.h> #include <linux/firmware.h> #include <linux/vmalloc.h> #include "ngene.h" -#include "stv6110x.h" -#include "stv090x.h" -#include "lnbh24.h" - static int one_adapter = 1; module_param(one_adapter, int, 0444); MODULE_PARM_DESC(one_adapter, "Use only one adapter."); @@ -63,8 +57,6 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define dprintk if (debug) printk -#define DEVICE_NAME "ngene" - #define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) #define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr))) #define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) @@ -352,7 +344,7 @@ static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) return 0; } -static int ngene_command(struct ngene *dev, struct ngene_command *com) +int ngene_command(struct ngene *dev, struct ngene_command *com) { int result; @@ -363,55 +355,6 @@ static int ngene_command(struct ngene *dev, struct ngene_command *com) } -static int ngene_command_i2c_read(struct ngene *dev, u8 adr, - u8 *out, u8 outlen, u8 *in, u8 inlen, int flag) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_I2C_READ; - com.cmd.hdr.Length = outlen + 3; - com.cmd.I2CRead.Device = adr << 1; - memcpy(com.cmd.I2CRead.Data, out, outlen); - com.cmd.I2CRead.Data[outlen] = inlen; - com.cmd.I2CRead.Data[outlen + 1] = 0; - com.in_len = outlen + 3; - com.out_len = inlen + 1; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - if ((com.cmd.raw8[0] >> 1) != adr) - return -EIO; - - if (flag) - memcpy(in, com.cmd.raw8, inlen + 1); - else - memcpy(in, com.cmd.raw8 + 1, inlen); - return 0; -} - -static int ngene_command_i2c_write(struct ngene *dev, u8 adr, - u8 *out, u8 outlen) -{ - struct ngene_command com; - - - com.cmd.hdr.Opcode = CMD_I2C_WRITE; - com.cmd.hdr.Length = outlen + 1; - com.cmd.I2CRead.Device = adr << 1; - memcpy(com.cmd.I2CRead.Data, out, outlen); - com.in_len = outlen + 1; - com.out_len = 1; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - if (com.cmd.raw8[0] == 1) - return -EIO; - - return 0; -} - static int ngene_command_load_firmware(struct ngene *dev, u8 *ngene_fw, u32 size) { @@ -477,7 +420,7 @@ static int ngene_command_config_free_buf(struct ngene *dev, u8 *config) return 0; } -static int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) +int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) { struct ngene_command com; @@ -514,11 +457,12 @@ static int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) /****************************************************************************/ -static u8 TSFeatureDecoderSetup[8 * 4] = { +static u8 TSFeatureDecoderSetup[8 * 5] = { 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */ 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */ 0x72, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */ + 0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */ }; /* Set NGENE I2S Config to 16 bit packed */ @@ -559,7 +503,7 @@ static u8 ITUFeatureDecoderSetup[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00 }; -static void FillTSBuffer(void *Buffer, int Length, u32 Flags) +void FillTSBuffer(void *Buffer, int Length, u32 Flags) { u32 *ptr = Buffer; @@ -772,144 +716,7 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream, return 0; } - -/****************************************************************************/ -/* I2C **********************************************************************/ -/****************************************************************************/ - -static void ngene_i2c_set_bus(struct ngene *dev, int bus) -{ - if (!(dev->card_info->i2c_access & 2)) - return; - if (dev->i2c_current_bus == bus) - return; - - switch (bus) { - case 0: - ngene_command_gpio_set(dev, 3, 0); - ngene_command_gpio_set(dev, 2, 1); - break; - - case 1: - ngene_command_gpio_set(dev, 2, 0); - ngene_command_gpio_set(dev, 3, 1); - break; - } - dev->i2c_current_bus = bus; -} - -static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, - struct i2c_msg msg[], int num) -{ - struct ngene_channel *chan = - (struct ngene_channel *)i2c_get_adapdata(adapter); - struct ngene *dev = chan->dev; - - down(&dev->i2c_switch_mutex); - ngene_i2c_set_bus(dev, chan->number); - - if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) - if (!ngene_command_i2c_read(dev, msg[0].addr, - msg[0].buf, msg[0].len, - msg[1].buf, msg[1].len, 0)) - goto done; - - if (num == 1 && !(msg[0].flags & I2C_M_RD)) - if (!ngene_command_i2c_write(dev, msg[0].addr, - msg[0].buf, msg[0].len)) - goto done; - if (num == 1 && (msg[0].flags & I2C_M_RD)) - if (!ngene_command_i2c_read(dev, msg[0].addr, 0, 0, - msg[0].buf, msg[0].len, 0)) - goto done; - - up(&dev->i2c_switch_mutex); - return -EIO; - -done: - up(&dev->i2c_switch_mutex); - return num; -} - - -static u32 ngene_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static struct i2c_algorithm ngene_i2c_algo = { - .master_xfer = ngene_i2c_master_xfer, - .functionality = ngene_i2c_functionality, -}; - -static int ngene_i2c_init(struct ngene *dev, int dev_nr) -{ - struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); - - i2c_set_adapdata(adap, &(dev->channel[dev_nr])); - adap->class = I2C_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG; - - strcpy(adap->name, "nGene"); - - adap->algo = &ngene_i2c_algo; - adap->algo_data = (void *)&(dev->channel[dev_nr]); - adap->dev.parent = &dev->pci_dev->dev; - - return i2c_add_adapter(adap); -} - - -/****************************************************************************/ -/* DVB functions and API interface ******************************************/ -/****************************************************************************/ - -static void swap_buffer(u32 *p, u32 len) -{ - while (len) { - *p = swab32(*p); - p++; - len -= 4; - } -} - - -static void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) -{ - struct ngene_channel *chan = priv; - - -#ifdef COMMAND_TIMEOUT_WORKAROUND - if (chan->users > 0) -#endif - dvb_dmx_swfilter(&chan->demux, buf, len); - return 0; -} - -u8 fill_ts[188] = { 0x47, 0x1f, 0xff, 0x10 }; - -static void *tsout_exchange(void *priv, void *buf, u32 len, - u32 clock, u32 flags) -{ - struct ngene_channel *chan = priv; - struct ngene *dev = chan->dev; - u32 alen; - - alen = dvb_ringbuffer_avail(&dev->tsout_rbuf); - alen -= alen % 188; - - if (alen < len) - FillTSBuffer(buf + alen, len - alen, flags); - else - alen = len; - dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen); - if (flags & DF_SWAP32) - swap_buffer((u32 *)buf, alen); - wake_up_interruptible(&dev->tsout_rbuf.queue); - return buf; -} - - -static void set_transfer(struct ngene_channel *chan, int state) +void set_transfer(struct ngene_channel *chan, int state) { u8 control = 0, mode = 0, flags = 0; struct ngene *dev = chan->dev; @@ -970,85 +777,12 @@ static void set_transfer(struct ngene_channel *chan, int state) state); if (!state) { spin_lock_irq(&chan->state_lock); - chan->pBufferExchange = 0; + chan->pBufferExchange = NULL; dvb_ringbuffer_flush(&dev->tsout_rbuf); spin_unlock_irq(&chan->state_lock); } } -static int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct ngene_channel *chan = dvbdmx->priv; - - if (chan->users == 0) { -#ifdef COMMAND_TIMEOUT_WORKAROUND - if (!chan->running) -#endif - set_transfer(chan, 1); - /* msleep(10); */ - } - - return ++chan->users; -} - -static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct ngene_channel *chan = dvbdmx->priv; - - if (--chan->users) - return chan->users; - -#ifndef COMMAND_TIMEOUT_WORKAROUND - set_transfer(chan, 0); -#endif - - return 0; -} - - - -static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, - int (*start_feed)(struct dvb_demux_feed *), - int (*stop_feed)(struct dvb_demux_feed *), - void *priv) -{ - dvbdemux->priv = priv; - - dvbdemux->filternum = 256; - dvbdemux->feednum = 256; - dvbdemux->start_feed = start_feed; - dvbdemux->stop_feed = stop_feed; - dvbdemux->write_to_decoder = 0; - dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING); - return dvb_dmx_init(dvbdemux); -} - -static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, - struct dvb_demux *dvbdemux, - struct dmx_frontend *hw_frontend, - struct dmx_frontend *mem_frontend, - struct dvb_adapter *dvb_adapter) -{ - int ret; - - dmxdev->filternum = 256; - dmxdev->demux = &dvbdemux->dmx; - dmxdev->capabilities = 0; - ret = dvb_dmxdev_init(dmxdev, dvb_adapter); - if (ret < 0) - return ret; - - hw_frontend->source = DMX_FRONTEND_0; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); - mem_frontend->source = DMX_MEMORY_FE; - dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); - return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); -} - /****************************************************************************/ /* nGene hardware init and release functions ********************************/ @@ -1094,8 +828,8 @@ static void free_idlebuffer(struct ngene *dev, return; free_ringbuffer(dev, rb); for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) { - Cur->Buffer2 = 0; - Cur->scList2 = 0; + Cur->Buffer2 = NULL; + Cur->scList2 = NULL; Cur->ngeneBuffer.Address_of_first_entry_2 = 0; Cur->ngeneBuffer.Number_of_entries_2 = 0; } @@ -1141,7 +875,7 @@ static int create_ring_buffer(struct pci_dev *pci_dev, u64 PARingBufferNext; struct SBufferHeader *Cur, *Next; - descr->Head = 0; + descr->Head = NULL; descr->MemSize = 0; descr->PAHead = 0; descr->NumBuffers = 0; @@ -1633,69 +1367,6 @@ fail: -/****************************************************************************/ -/* Switch control (I2C gates, etc.) *****************************************/ -/****************************************************************************/ - - -/****************************************************************************/ -/* Demod/tuner attachment ***************************************************/ -/****************************************************************************/ - -static int tuner_attach_stv6110(struct ngene_channel *chan) -{ - struct stv090x_config *feconf = (struct stv090x_config *) - chan->dev->card_info->fe_config[chan->number]; - struct stv6110x_config *tunerconf = (struct stv6110x_config *) - chan->dev->card_info->tuner_config[chan->number]; - struct stv6110x_devctl *ctl; - - ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, - &chan->i2c_adapter); - if (ctl == NULL) { - printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); - return -ENODEV; - } - - feconf->tuner_init = ctl->tuner_init; - feconf->tuner_set_mode = ctl->tuner_set_mode; - feconf->tuner_set_frequency = ctl->tuner_set_frequency; - feconf->tuner_get_frequency = ctl->tuner_get_frequency; - feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; - feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; - feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; - feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; - feconf->tuner_set_refclk = ctl->tuner_set_refclk; - feconf->tuner_get_status = ctl->tuner_get_status; - - return 0; -} - - -static int demod_attach_stv0900(struct ngene_channel *chan) -{ - struct stv090x_config *feconf = (struct stv090x_config *) - chan->dev->card_info->fe_config[chan->number]; - - chan->fe = dvb_attach(stv090x_attach, - feconf, - &chan->i2c_adapter, - chan->number == 0 ? STV090x_DEMODULATOR_0 : - STV090x_DEMODULATOR_1); - if (chan->fe == NULL) { - printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); - return -ENODEV; - } - - if (!dvb_attach(lnbh24_attach, chan->fe, &chan->i2c_adapter, 0, - 0, chan->dev->card_info->lnb[chan->number])) { - printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); - dvb_frontend_detach(chan->fe); - return -ENODEV; - } - - return 0; -} /****************************************************************************/ /****************************************************************************/ @@ -1719,7 +1390,7 @@ static void release_channel(struct ngene_channel *chan) if (chan->fe) { dvb_unregister_frontend(chan->fe); dvb_frontend_detach(chan->fe); - chan->fe = 0; + chan->fe = NULL; } dvbdemux->dmx.close(&dvbdemux->dmx); dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, @@ -1751,7 +1422,7 @@ static int init_channel(struct ngene_channel *chan) if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { if (nr >= STREAM_AUDIOIN1) chan->DataFormatFlags = DF_SWAP32; - if (nr == 0 || !one_adapter) { + if (nr == 0 || !one_adapter || dev->first_adapter == NULL) { adapter = &dev->adapter[nr]; ret = dvb_register_adapter(adapter, "nGene", THIS_MODULE, @@ -1759,8 +1430,10 @@ static int init_channel(struct ngene_channel *chan) adapter_nr); if (ret < 0) return ret; + if (dev->first_adapter == NULL) + dev->first_adapter = adapter; } else { - adapter = &dev->adapter[0]; + adapter = dev->first_adapter; } ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", @@ -1797,6 +1470,7 @@ static int init_channels(struct ngene *dev) int i, j; for (i = 0; i < MAX_STREAM; i++) { + dev->channel[i].number = i; if (init_channel(&dev->channel[i]) < 0) { for (j = i - 1; j >= 0; j--) release_channel(&dev->channel[j]); @@ -1810,7 +1484,7 @@ static int init_channels(struct ngene *dev) /* device probe/remove calls ************************************************/ /****************************************************************************/ -static void __devexit ngene_remove(struct pci_dev *pdev) +void __devexit ngene_remove(struct pci_dev *pdev) { struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev); int i; @@ -1820,12 +1494,12 @@ static void __devexit ngene_remove(struct pci_dev *pdev) release_channel(&dev->channel[i]); ngene_stop(dev); ngene_release_buffers(dev); - pci_set_drvdata(pdev, 0); + pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); } -static int __devinit ngene_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id) +int __devinit ngene_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) { struct ngene *dev; int stat = 0; @@ -1868,156 +1542,6 @@ fail1: ngene_release_buffers(dev); fail0: pci_disable_device(pci_dev); - pci_set_drvdata(pci_dev, 0); + pci_set_drvdata(pci_dev, NULL); return stat; } - -/****************************************************************************/ -/* Card configs *************************************************************/ -/****************************************************************************/ - -static struct stv090x_config fe_cineS2 = { - .device = STV0900, - .demod_mode = STV090x_DUAL, - .clk_mode = STV090x_CLK_EXT, - - .xtal = 27000000, - .address = 0x68, - - .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, - - .repeater_level = STV090x_RPTLEVEL_16, - - .adc1_range = STV090x_ADC_1Vpp, - .adc2_range = STV090x_ADC_1Vpp, - - .diseqc_envelope_mode = true, -}; - -static struct stv6110x_config tuner_cineS2_0 = { - .addr = 0x60, - .refclk = 27000000, - .clk_div = 1, -}; - -static struct stv6110x_config tuner_cineS2_1 = { - .addr = 0x63, - .refclk = 27000000, - .clk_div = 1, -}; - -static struct ngene_info ngene_info_cineS2 = { - .type = NGENE_SIDEWINDER, - .name = "Linux4Media cineS2 DVB-S2 Twin Tuner", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, - .fe_config = {&fe_cineS2, &fe_cineS2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0b, 0x08}, - .tsf = {3, 3}, - .fw_version = 15, -}; - -static struct ngene_info ngene_info_satixs2 = { - .type = NGENE_SIDEWINDER, - .name = "Mystique SaTiX-S2 Dual", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, - .fe_config = {&fe_cineS2, &fe_cineS2}, - .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, - .lnb = {0x0b, 0x08}, - .tsf = {3, 3}, - .fw_version = 15, -}; - -/****************************************************************************/ - - - -/****************************************************************************/ -/* PCI Subsystem ID *********************************************************/ -/****************************************************************************/ - -#define NGENE_ID(_subvend, _subdev, _driverdata) { \ - .vendor = NGENE_VID, .device = NGENE_PID, \ - .subvendor = _subvend, .subdevice = _subdev, \ - .driver_data = (unsigned long) &_driverdata } - -/****************************************************************************/ - -static const struct pci_device_id ngene_id_tbl[] __devinitdata = { - NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), - NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), - NGENE_ID(0x18c3, 0xdb01, ngene_info_satixs2), - {0} -}; -MODULE_DEVICE_TABLE(pci, ngene_id_tbl); - -/****************************************************************************/ -/* Init/Exit ****************************************************************/ -/****************************************************************************/ - -static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, - enum pci_channel_state state) -{ - printk(KERN_ERR DEVICE_NAME ": PCI error\n"); - if (state == pci_channel_io_perm_failure) - return PCI_ERS_RESULT_DISCONNECT; - if (state == pci_channel_io_frozen) - return PCI_ERS_RESULT_NEED_RESET; - return PCI_ERS_RESULT_CAN_RECOVER; -} - -static pci_ers_result_t ngene_link_reset(struct pci_dev *dev) -{ - printk(KERN_INFO DEVICE_NAME ": link reset\n"); - return 0; -} - -static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev) -{ - printk(KERN_INFO DEVICE_NAME ": slot reset\n"); - return 0; -} - -static void ngene_resume(struct pci_dev *dev) -{ - printk(KERN_INFO DEVICE_NAME ": resume\n"); -} - -static struct pci_error_handlers ngene_errors = { - .error_detected = ngene_error_detected, - .link_reset = ngene_link_reset, - .slot_reset = ngene_slot_reset, - .resume = ngene_resume, -}; - -static struct pci_driver ngene_pci_driver = { - .name = "ngene", - .id_table = ngene_id_tbl, - .probe = ngene_probe, - .remove = __devexit_p(ngene_remove), - .err_handler = &ngene_errors, -}; - -static __init int module_init_ngene(void) -{ - printk(KERN_INFO - "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); - return pci_register_driver(&ngene_pci_driver); -} - -static __exit void module_exit_ngene(void) -{ - pci_unregister_driver(&ngene_pci_driver); -} - -module_init(module_init_ngene); -module_exit(module_exit_ngene); - -MODULE_DESCRIPTION("nGene"); -MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/dvb/ngene/ngene-dvb.c new file mode 100644 index 000000000000..96013eb353cd --- /dev/null +++ b/drivers/media/dvb/ngene/ngene-dvb.c @@ -0,0 +1,172 @@ +/* + * ngene-dvb.c: nGene PCIe bridge driver - DVB functions + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de> + * Modifications for new nGene firmware, + * support for EEPROM-copying, + * support for new dual DVB-S2 card prototype + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/io.h> +#include <asm/div64.h> +#include <linux/pci.h> +#include <linux/smp_lock.h> +#include <linux/timer.h> +#include <linux/version.h> +#include <linux/byteorder/generic.h> +#include <linux/firmware.h> +#include <linux/vmalloc.h> + +#include "ngene.h" + +#define COMMAND_TIMEOUT_WORKAROUND + + +/****************************************************************************/ +/* COMMAND API interface ****************************************************/ +/****************************************************************************/ + +/****************************************************************************/ +/* DVB functions and API interface ******************************************/ +/****************************************************************************/ + +static void swap_buffer(u32 *p, u32 len) +{ + while (len) { + *p = swab32(*p); + p++; + len -= 4; + } +} + +void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + + +#ifdef COMMAND_TIMEOUT_WORKAROUND + if (chan->users > 0) +#endif + dvb_dmx_swfilter(&chan->demux, buf, len); + return NULL; +} + +u8 fill_ts[188] = { 0x47, 0x1f, 0xff, 0x10 }; + +void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + struct ngene *dev = chan->dev; + u32 alen; + + alen = dvb_ringbuffer_avail(&dev->tsout_rbuf); + alen -= alen % 188; + + if (alen < len) + FillTSBuffer(buf + alen, len - alen, flags); + else + alen = len; + dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen); + if (flags & DF_SWAP32) + swap_buffer((u32 *)buf, alen); + wake_up_interruptible(&dev->tsout_rbuf.queue); + return buf; +} + + + +int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ngene_channel *chan = dvbdmx->priv; + + if (chan->users == 0) { +#ifdef COMMAND_TIMEOUT_WORKAROUND + if (!chan->running) +#endif + set_transfer(chan, 1); + /* msleep(10); */ + } + + return ++chan->users; +} + +int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ngene_channel *chan = dvbdmx->priv; + + if (--chan->users) + return chan->users; + +#ifndef COMMAND_TIMEOUT_WORKAROUND + set_transfer(chan, 0); +#endif + + return 0; +} + +int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, + int (*start_feed)(struct dvb_demux_feed *), + int (*stop_feed)(struct dvb_demux_feed *), + void *priv) +{ + dvbdemux->priv = priv; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = start_feed; + dvbdemux->stop_feed = stop_feed; + dvbdemux->write_to_decoder = NULL; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + return dvb_dmx_init(dvbdemux); +} + +int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, + struct dvb_demux *dvbdemux, + struct dmx_frontend *hw_frontend, + struct dmx_frontend *mem_frontend, + struct dvb_adapter *dvb_adapter) +{ + int ret; + + dmxdev->filternum = 256; + dmxdev->demux = &dvbdemux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adapter); + if (ret < 0) + return ret; + + hw_frontend->source = DMX_FRONTEND_0; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); + mem_frontend->source = DMX_MEMORY_FE; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); + return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); +} diff --git a/drivers/media/dvb/ngene/ngene-i2c.c b/drivers/media/dvb/ngene/ngene-i2c.c new file mode 100644 index 000000000000..2ef54ca6badd --- /dev/null +++ b/drivers/media/dvb/ngene/ngene-i2c.c @@ -0,0 +1,179 @@ +/* + * ngene-i2c.c: nGene PCIe bridge driver i2c functions + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de> + * Modifications for new nGene firmware, + * support for EEPROM-copying, + * support for new dual DVB-S2 card prototype + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +/* FIXME - some of these can probably be removed */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/io.h> +#include <asm/div64.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/smp_lock.h> +#include <linux/timer.h> +#include <linux/version.h> +#include <linux/byteorder/generic.h> +#include <linux/firmware.h> +#include <linux/vmalloc.h> + +#include "ngene.h" + +/* Firmware command for i2c operations */ +static int ngene_command_i2c_read(struct ngene *dev, u8 adr, + u8 *out, u8 outlen, u8 *in, u8 inlen, int flag) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_I2C_READ; + com.cmd.hdr.Length = outlen + 3; + com.cmd.I2CRead.Device = adr << 1; + memcpy(com.cmd.I2CRead.Data, out, outlen); + com.cmd.I2CRead.Data[outlen] = inlen; + com.cmd.I2CRead.Data[outlen + 1] = 0; + com.in_len = outlen + 3; + com.out_len = inlen + 1; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + if ((com.cmd.raw8[0] >> 1) != adr) + return -EIO; + + if (flag) + memcpy(in, com.cmd.raw8, inlen + 1); + else + memcpy(in, com.cmd.raw8 + 1, inlen); + return 0; +} + +static int ngene_command_i2c_write(struct ngene *dev, u8 adr, + u8 *out, u8 outlen) +{ + struct ngene_command com; + + + com.cmd.hdr.Opcode = CMD_I2C_WRITE; + com.cmd.hdr.Length = outlen + 1; + com.cmd.I2CRead.Device = adr << 1; + memcpy(com.cmd.I2CRead.Data, out, outlen); + com.in_len = outlen + 1; + com.out_len = 1; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + if (com.cmd.raw8[0] == 1) + return -EIO; + + return 0; +} + +static void ngene_i2c_set_bus(struct ngene *dev, int bus) +{ + if (!(dev->card_info->i2c_access & 2)) + return; + if (dev->i2c_current_bus == bus) + return; + + switch (bus) { + case 0: + ngene_command_gpio_set(dev, 3, 0); + ngene_command_gpio_set(dev, 2, 1); + break; + + case 1: + ngene_command_gpio_set(dev, 2, 0); + ngene_command_gpio_set(dev, 3, 1); + break; + } + dev->i2c_current_bus = bus; +} + +static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msg[], int num) +{ + struct ngene_channel *chan = + (struct ngene_channel *)i2c_get_adapdata(adapter); + struct ngene *dev = chan->dev; + + down(&dev->i2c_switch_mutex); + ngene_i2c_set_bus(dev, chan->number); + + if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_read(dev, msg[0].addr, + msg[0].buf, msg[0].len, + msg[1].buf, msg[1].len, 0)) + goto done; + + if (num == 1 && !(msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_write(dev, msg[0].addr, + msg[0].buf, msg[0].len)) + goto done; + if (num == 1 && (msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_read(dev, msg[0].addr, NULL, 0, + msg[0].buf, msg[0].len, 0)) + goto done; + + up(&dev->i2c_switch_mutex); + return -EIO; + +done: + up(&dev->i2c_switch_mutex); + return num; +} + + +static u32 ngene_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ngene_i2c_algo = { + .master_xfer = ngene_i2c_master_xfer, + .functionality = ngene_i2c_functionality, +}; + +int ngene_i2c_init(struct ngene *dev, int dev_nr) +{ + struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); + + i2c_set_adapdata(adap, &(dev->channel[dev_nr])); + adap->class = I2C_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG; + + strcpy(adap->name, "nGene"); + + adap->algo = &ngene_i2c_algo; + adap->algo_data = (void *)&(dev->channel[dev_nr]); + adap->dev.parent = &dev->pci_dev->dev; + + return i2c_add_adapter(adap); +} + diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index a7eb29846310..676fcbb79026 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -39,6 +39,8 @@ #include "dvb_frontend.h" #include "dvb_ringbuffer.h" +#define DEVICE_NAME "ngene" + #define NGENE_VID 0x18c3 #define NGENE_PID 0x0720 @@ -752,6 +754,7 @@ struct ngene { spinlock_t cmd_lock; struct dvb_adapter adapter[MAX_STREAM]; + struct dvb_adapter *first_adapter; /* "one_adapter" modprobe opt */ struct ngene_channel channel[MAX_STREAM]; struct ngene_info *card_info; @@ -853,6 +856,33 @@ struct ngene_buffer { #endif +/* Provided by ngene-core.c */ +int __devinit ngene_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id); +void __devexit ngene_remove(struct pci_dev *pdev); +int ngene_command(struct ngene *dev, struct ngene_command *com); +int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level); +void set_transfer(struct ngene_channel *chan, int state); +void FillTSBuffer(void *Buffer, int Length, u32 Flags); + +/* Provided by ngene-i2c.c */ +int ngene_i2c_init(struct ngene *dev, int dev_nr); + +/* Provided by ngene-dvb.c */ +void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); +void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); +int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed); +int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, + int (*start_feed)(struct dvb_demux_feed *), + int (*stop_feed)(struct dvb_demux_feed *), + void *priv); +int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, + struct dvb_demux *dvbdemux, + struct dmx_frontend *hw_frontend, + struct dmx_frontend *mem_frontend, + struct dvb_adapter *dvb_adapter); + #endif /* LocalWords: Endif diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c index 6aded234aa61..69ad94934ec2 100644 --- a/drivers/media/dvb/pt1/pt1.c +++ b/drivers/media/dvb/pt1/pt1.c @@ -1,5 +1,5 @@ /* - * driver for Earthsoft PT1 + * driver for Earthsoft PT1/PT2 * * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> * @@ -77,6 +77,10 @@ struct pt1 { struct pt1_adapter *adaps[PT1_NR_ADAPS]; struct pt1_table *tables; struct task_struct *kthread; + + struct mutex lock; + int power; + int reset; }; struct pt1_adapter { @@ -95,6 +99,11 @@ struct pt1_adapter { struct dvb_frontend *fe; int (*orig_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + int (*orig_sleep)(struct dvb_frontend *fe); + int (*orig_init)(struct dvb_frontend *fe); + + fe_sec_voltage_t voltage; + int sleep; }; #define pt1_printk(level, pt1, format, arg...) \ @@ -219,8 +228,10 @@ static int pt1_do_enable_ram(struct pt1 *pt1) static int pt1_enable_ram(struct pt1 *pt1) { int i, ret; + int phase; schedule_timeout_uninterruptible((HZ + 999) / 1000); - for (i = 0; i < 10; i++) { + phase = pt1->pdev->device == 0x211a ? 128 : 166; + for (i = 0; i < phase; i++) { ret = pt1_do_enable_ram(pt1); if (ret < 0) return ret; @@ -485,33 +496,47 @@ static int pt1_stop_feed(struct dvb_demux_feed *feed) } static void -pt1_set_power(struct pt1 *pt1, int power, int lnb, int reset) +pt1_update_power(struct pt1 *pt1) { - pt1_write_reg(pt1, 1, power | lnb << 1 | !reset << 3); + int bits; + int i; + struct pt1_adapter *adap; + static const int sleep_bits[] = { + 1 << 4, + 1 << 6 | 1 << 7, + 1 << 5, + 1 << 6 | 1 << 8, + }; + + bits = pt1->power | !pt1->reset << 3; + mutex_lock(&pt1->lock); + for (i = 0; i < PT1_NR_ADAPS; i++) { + adap = pt1->adaps[i]; + switch (adap->voltage) { + case SEC_VOLTAGE_13: /* actually 11V */ + bits |= 1 << 1; + break; + case SEC_VOLTAGE_18: /* actually 15V */ + bits |= 1 << 1 | 1 << 2; + break; + default: + break; + } + + /* XXX: The bits should be changed depending on adap->sleep. */ + bits |= sleep_bits[i]; + } + pt1_write_reg(pt1, 1, bits); + mutex_unlock(&pt1->lock); } static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct pt1_adapter *adap; - int lnb; adap = container_of(fe->dvb, struct pt1_adapter, adap); - - switch (voltage) { - case SEC_VOLTAGE_13: /* actually 11V */ - lnb = 2; - break; - case SEC_VOLTAGE_18: /* actually 15V */ - lnb = 3; - break; - case SEC_VOLTAGE_OFF: - lnb = 0; - break; - default: - return -EINVAL; - } - - pt1_set_power(adap->pt1, 1, lnb, 0); + adap->voltage = voltage; + pt1_update_power(adap->pt1); if (adap->orig_set_voltage) return adap->orig_set_voltage(fe, voltage); @@ -519,9 +544,37 @@ static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } +static int pt1_sleep(struct dvb_frontend *fe) +{ + struct pt1_adapter *adap; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + adap->sleep = 1; + pt1_update_power(adap->pt1); + + if (adap->orig_sleep) + return adap->orig_sleep(fe); + else + return 0; +} + +static int pt1_wakeup(struct dvb_frontend *fe) +{ + struct pt1_adapter *adap; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + adap->sleep = 0; + pt1_update_power(adap->pt1); + schedule_timeout_uninterruptible((HZ + 999) / 1000); + + if (adap->orig_init) + return adap->orig_init(fe); + else + return 0; +} + static void pt1_free_adapter(struct pt1_adapter *adap) { - dvb_unregister_frontend(adap->fe); dvb_net_release(&adap->net); adap->demux.dmx.close(&adap->demux.dmx); dvb_dmxdev_release(&adap->dmxdev); @@ -534,7 +587,7 @@ static void pt1_free_adapter(struct pt1_adapter *adap) DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static struct pt1_adapter * -pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe) +pt1_alloc_adapter(struct pt1 *pt1) { struct pt1_adapter *adap; void *buf; @@ -551,8 +604,8 @@ pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe) adap->pt1 = pt1; - adap->orig_set_voltage = fe->ops.set_voltage; - fe->ops.set_voltage = pt1_set_voltage; + adap->voltage = SEC_VOLTAGE_OFF; + adap->sleep = 1; buf = (u8 *)__get_free_page(GFP_KERNEL); if (!buf) { @@ -593,17 +646,8 @@ pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe) dvb_net_init(dvb_adap, &adap->net, &demux->dmx); - ret = dvb_register_frontend(dvb_adap, fe); - if (ret < 0) - goto err_net_release; - adap->fe = fe; - return adap; -err_net_release: - dvb_net_release(&adap->net); - adap->demux.dmx.close(&adap->demux.dmx); - dvb_dmxdev_release(&adap->dmxdev); err_dmx_release: dvb_dmx_release(demux); err_unregister_adapter: @@ -623,6 +667,62 @@ static void pt1_cleanup_adapters(struct pt1 *pt1) pt1_free_adapter(pt1->adaps[i]); } +static int pt1_init_adapters(struct pt1 *pt1) +{ + int i; + struct pt1_adapter *adap; + int ret; + + for (i = 0; i < PT1_NR_ADAPS; i++) { + adap = pt1_alloc_adapter(pt1); + if (IS_ERR(adap)) { + ret = PTR_ERR(adap); + goto err; + } + + adap->index = i; + pt1->adaps[i] = adap; + } + return 0; + +err: + while (i--) + pt1_free_adapter(pt1->adaps[i]); + + return ret; +} + +static void pt1_cleanup_frontend(struct pt1_adapter *adap) +{ + dvb_unregister_frontend(adap->fe); +} + +static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe) +{ + int ret; + + adap->orig_set_voltage = fe->ops.set_voltage; + adap->orig_sleep = fe->ops.sleep; + adap->orig_init = fe->ops.init; + fe->ops.set_voltage = pt1_set_voltage; + fe->ops.sleep = pt1_sleep; + fe->ops.init = pt1_wakeup; + + ret = dvb_register_frontend(&adap->adap, fe); + if (ret < 0) + return ret; + + adap->fe = fe; + return 0; +} + +static void pt1_cleanup_frontends(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_cleanup_frontend(pt1->adaps[i]); +} + struct pt1_config { struct va1j5jf8007s_config va1j5jf8007s_config; struct va1j5jf8007t_config va1j5jf8007t_config; @@ -630,29 +730,63 @@ struct pt1_config { static const struct pt1_config pt1_configs[2] = { { - { .demod_address = 0x1b }, - { .demod_address = 0x1a }, + { + .demod_address = 0x1b, + .frequency = VA1J5JF8007S_20MHZ, + }, + { + .demod_address = 0x1a, + .frequency = VA1J5JF8007T_20MHZ, + }, }, { - { .demod_address = 0x19 }, - { .demod_address = 0x18 }, + { + .demod_address = 0x19, + .frequency = VA1J5JF8007S_20MHZ, + }, + { + .demod_address = 0x18, + .frequency = VA1J5JF8007T_20MHZ, + }, }, }; -static int pt1_init_adapters(struct pt1 *pt1) +static const struct pt1_config pt2_configs[2] = { + { + { + .demod_address = 0x1b, + .frequency = VA1J5JF8007S_25MHZ, + }, + { + .demod_address = 0x1a, + .frequency = VA1J5JF8007T_25MHZ, + }, + }, { + { + .demod_address = 0x19, + .frequency = VA1J5JF8007S_25MHZ, + }, + { + .demod_address = 0x18, + .frequency = VA1J5JF8007T_25MHZ, + }, + }, +}; + +static int pt1_init_frontends(struct pt1 *pt1) { int i, j; struct i2c_adapter *i2c_adap; - const struct pt1_config *config; + const struct pt1_config *configs, *config; struct dvb_frontend *fe[4]; - struct pt1_adapter *adap; int ret; i = 0; j = 0; i2c_adap = &pt1->i2c_adap; + configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs; do { - config = &pt1_configs[i / 2]; + config = &configs[i / 2]; fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, i2c_adap); @@ -681,11 +815,9 @@ static int pt1_init_adapters(struct pt1 *pt1) } while (i < 4); do { - adap = pt1_alloc_adapter(pt1, fe[j]); - if (IS_ERR(adap)) + ret = pt1_init_frontend(pt1->adaps[j], fe[j]); + if (ret < 0) goto err; - adap->index = j; - pt1->adaps[j] = adap; } while (++j < 4); return 0; @@ -695,7 +827,7 @@ err: fe[i]->ops.release(fe[i]); while (j--) - pt1_free_adapter(pt1->adaps[j]); + dvb_unregister_frontend(fe[j]); return ret; } @@ -890,9 +1022,12 @@ static void __devexit pt1_remove(struct pci_dev *pdev) kthread_stop(pt1->kthread); pt1_cleanup_tables(pt1); - pt1_cleanup_adapters(pt1); + pt1_cleanup_frontends(pt1); pt1_disable_ram(pt1); - pt1_set_power(pt1, 0, 0, 1); + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); + pt1_cleanup_adapters(pt1); i2c_del_adapter(&pt1->i2c_adap); pci_set_drvdata(pdev, NULL); kfree(pt1); @@ -936,10 +1071,21 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_pci_iounmap; } + mutex_init(&pt1->lock); pt1->pdev = pdev; pt1->regs = regs; pci_set_drvdata(pdev, pt1); + ret = pt1_init_adapters(pt1); + if (ret < 0) + goto err_kfree; + + mutex_init(&pt1->lock); + + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); + i2c_adap = &pt1->i2c_adap; i2c_adap->class = I2C_CLASS_TV_DIGITAL; i2c_adap->algo = &pt1_i2c_algo; @@ -948,9 +1094,7 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i2c_set_adapdata(i2c_adap, pt1); ret = i2c_add_adapter(i2c_adap); if (ret < 0) - goto err_kfree; - - pt1_set_power(pt1, 0, 0, 1); + goto err_pt1_cleanup_adapters; pt1_i2c_init(pt1); pt1_i2c_wait(pt1); @@ -979,19 +1123,21 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pt1_init_streams(pt1); - pt1_set_power(pt1, 1, 0, 1); + pt1->power = 1; + pt1_update_power(pt1); schedule_timeout_uninterruptible((HZ + 49) / 50); - pt1_set_power(pt1, 1, 0, 0); + pt1->reset = 0; + pt1_update_power(pt1); schedule_timeout_uninterruptible((HZ + 999) / 1000); - ret = pt1_init_adapters(pt1); + ret = pt1_init_frontends(pt1); if (ret < 0) goto err_pt1_disable_ram; ret = pt1_init_tables(pt1); if (ret < 0) - goto err_pt1_cleanup_adapters; + goto err_pt1_cleanup_frontends; kthread = kthread_run(pt1_thread, pt1, "pt1"); if (IS_ERR(kthread)) { @@ -1004,11 +1150,15 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_pt1_cleanup_tables: pt1_cleanup_tables(pt1); -err_pt1_cleanup_adapters: - pt1_cleanup_adapters(pt1); +err_pt1_cleanup_frontends: + pt1_cleanup_frontends(pt1); err_pt1_disable_ram: pt1_disable_ram(pt1); - pt1_set_power(pt1, 0, 0, 1); + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); +err_pt1_cleanup_adapters: + pt1_cleanup_adapters(pt1); err_i2c_del_adapter: i2c_del_adapter(i2c_adap); err_kfree: @@ -1027,6 +1177,7 @@ err: static struct pci_device_id pt1_id_table[] = { { PCI_DEVICE(0x10ee, 0x211a) }, + { PCI_DEVICE(0x10ee, 0x222a) }, { }, }; MODULE_DEVICE_TABLE(pci, pt1_id_table); @@ -1054,5 +1205,5 @@ module_init(pt1_init); module_exit(pt1_cleanup); MODULE_AUTHOR("Takahito HIRANO <hiranotaka@zng.info>"); -MODULE_DESCRIPTION("Earthsoft PT1 Driver"); +MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c index fc6594996e79..451641c0c1d2 100644 --- a/drivers/media/dvb/pt1/va1j5jf8007s.c +++ b/drivers/media/dvb/pt1/va1j5jf8007s.c @@ -1,5 +1,5 @@ /* - * ISDB-S driver for VA1J5JF8007 + * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 * * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> * @@ -580,7 +580,7 @@ static void va1j5jf8007s_release(struct dvb_frontend *fe) static struct dvb_frontend_ops va1j5jf8007s_ops = { .info = { - .name = "VA1J5JF8007 ISDB-S", + .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S", .type = FE_QPSK, .frequency_min = 950000, .frequency_max = 2150000, @@ -628,28 +628,50 @@ static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state) return 0; } -static const u8 va1j5jf8007s_prepare_bufs[][2] = { +static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = { {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, }; +static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = { + {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a}, + {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89}, + {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04}, + {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0}, +}; + static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state) { + const u8 (*bufs)[2]; + int size; u8 addr; u8 buf[2]; struct i2c_msg msg; int i; + switch (state->config->frequency) { + case VA1J5JF8007S_20MHZ: + bufs = va1j5jf8007s_20mhz_prepare_bufs; + size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs); + break; + case VA1J5JF8007S_25MHZ: + bufs = va1j5jf8007s_25mhz_prepare_bufs; + size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs); + break; + default: + return -EINVAL; + } + addr = state->config->demod_address; msg.addr = addr; msg.flags = 0; msg.len = 2; msg.buf = buf; - for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_prepare_bufs); i++) { - memcpy(buf, va1j5jf8007s_prepare_bufs[i], sizeof(buf)); + for (i = 0; i < size; i++) { + memcpy(buf, bufs[i], sizeof(buf)); if (i2c_transfer(state->adap, &msg, 1) != 1) return -EREMOTEIO; } diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.h b/drivers/media/dvb/pt1/va1j5jf8007s.h index aa228a816353..b7d6f05a0e02 100644 --- a/drivers/media/dvb/pt1/va1j5jf8007s.h +++ b/drivers/media/dvb/pt1/va1j5jf8007s.h @@ -1,5 +1,5 @@ /* - * ISDB-S driver for VA1J5JF8007 + * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 * * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> * @@ -24,8 +24,14 @@ #ifndef VA1J5JF8007S_H #define VA1J5JF8007S_H +enum va1j5jf8007s_frequency { + VA1J5JF8007S_20MHZ, + VA1J5JF8007S_25MHZ, +}; + struct va1j5jf8007s_config { u8 demod_address; + enum va1j5jf8007s_frequency frequency; }; struct i2c_adapter; diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/dvb/pt1/va1j5jf8007t.c index 3db4f3e34e8f..0f085c3e571b 100644 --- a/drivers/media/dvb/pt1/va1j5jf8007t.c +++ b/drivers/media/dvb/pt1/va1j5jf8007t.c @@ -1,5 +1,5 @@ /* - * ISDB-T driver for VA1J5JF8007 + * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 * * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> * @@ -429,7 +429,7 @@ static void va1j5jf8007t_release(struct dvb_frontend *fe) static struct dvb_frontend_ops va1j5jf8007t_ops = { .info = { - .name = "VA1J5JF8007 ISDB-T", + .name = "VA1J5JF8007/VA1J5JF8011 ISDB-T", .type = FE_OFDM, .frequency_min = 90000000, .frequency_max = 770000000, @@ -448,29 +448,50 @@ static struct dvb_frontend_ops va1j5jf8007t_ops = { .release = va1j5jf8007t_release, }; -static const u8 va1j5jf8007t_prepare_bufs[][2] = { +static const u8 va1j5jf8007t_20mhz_prepare_bufs[][2] = { {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, {0xef, 0x01} }; +static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = { + {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83}, + {0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c}, + {0x77, 0x03}, {0xef, 0x01} +}; + int va1j5jf8007t_prepare(struct dvb_frontend *fe) { struct va1j5jf8007t_state *state; + const u8 (*bufs)[2]; + int size; u8 buf[2]; struct i2c_msg msg; int i; state = fe->demodulator_priv; + switch (state->config->frequency) { + case VA1J5JF8007T_20MHZ: + bufs = va1j5jf8007t_20mhz_prepare_bufs; + size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs); + break; + case VA1J5JF8007T_25MHZ: + bufs = va1j5jf8007t_25mhz_prepare_bufs; + size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs); + break; + default: + return -EINVAL; + } + msg.addr = state->config->demod_address; msg.flags = 0; msg.len = sizeof(buf); msg.buf = buf; - for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_prepare_bufs); i++) { - memcpy(buf, va1j5jf8007t_prepare_bufs[i], sizeof(buf)); + for (i = 0; i < size; i++) { + memcpy(buf, bufs[i], sizeof(buf)); if (i2c_transfer(state->adap, &msg, 1) != 1) return -EREMOTEIO; } diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.h b/drivers/media/dvb/pt1/va1j5jf8007t.h index ed49906f7769..2903be519ef5 100644 --- a/drivers/media/dvb/pt1/va1j5jf8007t.h +++ b/drivers/media/dvb/pt1/va1j5jf8007t.h @@ -1,5 +1,5 @@ /* - * ISDB-T driver for VA1J5JF8007 + * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 * * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> * @@ -24,8 +24,14 @@ #ifndef VA1J5JF8007T_H #define VA1J5JF8007T_H +enum va1j5jf8007t_frequency { + VA1J5JF8007T_20MHZ, + VA1J5JF8007T_25MHZ, +}; + struct va1j5jf8007t_config { u8 demod_address; + enum va1j5jf8007t_frequency frequency; }; struct i2c_adapter; diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 49c2a817a06f..461714396331 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -35,7 +35,7 @@ #include <linux/interrupt.h> #include <linux/input.h> #include <linux/spinlock.h> -#include <media/ir-common.h> +#include <media/ir-core.h> #include "budget.h" @@ -54,6 +54,8 @@ #include "tda1002x.h" #include "tda827x.h" +#define MODULE_NAME "budget_ci" + /* * Regarding DEBIADDR_IR: * Some CI modules hang if random addresses are read. @@ -80,12 +82,6 @@ #define SLOTSTATUS_READY 8 #define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) -/* - * Milliseconds during which a key is regarded as pressed. - * If an identical command arrives within this time, the timer will start over. - */ -#define IR_KEYPRESS_TIMEOUT 250 - /* RC5 device wildcard */ #define IR_DEVICE_ANY 255 @@ -102,12 +98,9 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct budget_ci_ir { struct input_dev *dev; struct tasklet_struct msp430_irq_tasklet; - struct timer_list timer_keyup; char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ char phys[32]; - struct ir_input_state state; int rc5_device; - u32 last_raw; u32 ir_key; bool have_command; }; @@ -122,18 +115,11 @@ struct budget_ci { u8 tuner_pll_address; /* used for philips_tdm1316l configs */ }; -static void msp430_ir_keyup(unsigned long data) -{ - struct budget_ci_ir *ir = (struct budget_ci_ir *) data; - ir_input_nokey(ir->dev, &ir->state); -} - static void msp430_ir_interrupt(unsigned long data) { struct budget_ci *budget_ci = (struct budget_ci *) data; struct input_dev *dev = budget_ci->ir.dev; u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; - u32 raw; /* * The msp430 chip can generate two different bytes, command and device @@ -169,20 +155,12 @@ static void msp430_ir_interrupt(unsigned long data) return; budget_ci->ir.have_command = false; + /* FIXME: We should generate complete scancodes with device info */ if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != (command & 0x1f)) return; - /* Is this a repeated key sequence? (same device, command, toggle) */ - raw = budget_ci->ir.ir_key | (command << 8); - if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) { - ir_input_nokey(dev, &budget_ci->ir.state); - ir_input_keydown(dev, &budget_ci->ir.state, - budget_ci->ir.ir_key); - budget_ci->ir.last_raw = raw; - } - - mod_timer(&budget_ci->ir.timer_keyup, jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT)); + ir_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0); } static int msp430_ir_init(struct budget_ci *budget_ci) @@ -190,7 +168,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) struct saa7146_dev *saa = budget_ci->budget.dev; struct input_dev *input_dev = budget_ci->ir.dev; int error; - struct ir_scancode_table *ir_codes; + char *ir_codes = NULL; budget_ci->ir.dev = input_dev = input_allocate_device(); @@ -230,7 +208,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x1011: case 0x1012: /* The hauppauge keymap is a superset of these remotes */ - ir_codes = &ir_codes_hauppauge_new_table; + ir_codes = RC_MAP_HAUPPAUGE_NEW; if (rc5_device < 0) budget_ci->ir.rc5_device = 0x1f; @@ -239,22 +217,15 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x1017: case 0x101a: /* for the Technotrend 1500 bundled remote */ - ir_codes = &ir_codes_tt_1500_table; + ir_codes = RC_MAP_TT_1500; break; default: /* unknown remote */ - ir_codes = &ir_codes_budget_ci_old_table; + ir_codes = RC_MAP_BUDGET_CI_OLD; break; } - ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5); - - /* initialise the key-up timeout handler */ - init_timer(&budget_ci->ir.timer_keyup); - budget_ci->ir.timer_keyup.function = msp430_ir_keyup; - budget_ci->ir.timer_keyup.data = (unsigned long) &budget_ci->ir; - budget_ci->ir.last_raw = 0xffff; /* An impossible value */ - error = ir_input_register(input_dev, ir_codes, NULL); + error = ir_input_register(input_dev, ir_codes, NULL, MODULE_NAME); if (error) { printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); return error; @@ -282,9 +253,6 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); - del_timer_sync(&dev->timer); - ir_input_nokey(dev, &budget_ci->ir.state); - ir_input_unregister(dev); } diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c index 1500210c06cf..874a10a9d493 100644 --- a/drivers/media/dvb/ttpci/budget.c +++ b/drivers/media/dvb/ttpci/budget.c @@ -442,6 +442,7 @@ static struct stv090x_config tt1600_stv090x_config = { .repeater_level = STV090x_RPTLEVEL_16, .tuner_init = NULL, + .tuner_sleep = NULL, .tuner_set_mode = NULL, .tuner_set_frequency = NULL, .tuner_get_frequency = NULL, @@ -627,22 +628,36 @@ static void frontend_init(struct budget *budget) &tt1600_stv6110x_config, &budget->i2c_adap); - tt1600_stv090x_config.tuner_init = ctl->tuner_init; - tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; - tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; - tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; - tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; - tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; - tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; - tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; - tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; - tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; - - dvb_attach(isl6423_attach, - budget->dvb_frontend, - &budget->i2c_adap, - &tt1600_isl6423_config); - + if (ctl) { + tt1600_stv090x_config.tuner_init = ctl->tuner_init; + tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; + tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (budget->dvb_frontend->ops.init) + budget->dvb_frontend->ops.init(budget->dvb_frontend); + + if (dvb_attach(isl6423_attach, + budget->dvb_frontend, + &budget->i2c_adap, + &tt1600_isl6423_config) == NULL) { + printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__); + goto error_out; + } + } else { + printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); + goto error_out; + } } } break; diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 02a9cefc9a00..353b82855949 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -144,7 +144,10 @@ struct amradio_device { int initialized; }; -#define vdev_to_amradio(r) container_of(r, struct amradio_device, videodev) +static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct amradio_device, v4l2_dev); +} /* USB Device ID List */ static struct usb_device_id usb_amradio_device_table[] = { @@ -284,13 +287,12 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument) */ static void usb_amradio_disconnect(struct usb_interface *intf) { - struct amradio_device *radio = usb_get_intfdata(intf); + struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); radio->usbdev = NULL; mutex_unlock(&radio->lock); - usb_set_intfdata(intf, NULL); v4l2_device_disconnect(&radio->v4l2_dev); video_unregister_device(&radio->videodev); } @@ -500,7 +502,7 @@ out: /* open device - amradio_start() and amradio_setfreq() */ static int usb_amradio_open(struct file *file) { - struct amradio_device *radio = vdev_to_amradio(video_devdata(file)); + struct amradio_device *radio = video_drvdata(file); int retval = 0; mutex_lock(&radio->lock); @@ -566,7 +568,7 @@ unlock: /* Suspend device - stop device. Need to be checked and fixed */ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) { - struct amradio_device *radio = usb_get_intfdata(intf); + struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); @@ -584,7 +586,7 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) /* Resume device - start device. Need to be checked and fixed */ static int usb_amradio_resume(struct usb_interface *intf) { - struct amradio_device *radio = usb_get_intfdata(intf); + struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); @@ -633,9 +635,7 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { static void usb_amradio_video_device_release(struct video_device *videodev) { - struct amradio_device *radio = vdev_to_amradio(videodev); - - v4l2_device_unregister(&radio->v4l2_dev); + struct amradio_device *radio = video_get_drvdata(videodev); /* free rest memory */ kfree(radio->buffer); @@ -693,7 +693,6 @@ static int usb_amradio_probe(struct usb_interface *intf, goto err_vdev; } - usb_set_intfdata(intf, radio); return 0; err_vdev: diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9644cf760aaa..ad9e6f9c22e9 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -45,6 +45,10 @@ config VIDEO_TUNER tristate depends on MEDIA_TUNER +config V4L2_MEM2MEM_DEV + tristate + depends on VIDEOBUF_GEN + # # Multimedia Video device configuration # @@ -480,6 +484,12 @@ config VIDEO_ADV7343 To compile this driver as a module, choose M here: the module will be called adv7343. +config VIDEO_AK881X + tristate "AK8813/AK8814 video encoders" + depends on I2C + help + Video output driver for AKM AK8813 and AK8814 TV encoders + comment "Video improvement chips" config VIDEO_UPD64031A @@ -520,6 +530,13 @@ config DISPLAY_DAVINCI_DM646X_EVM To compile this driver as a module, choose M here: the module will be called vpif_display. +config VIDEO_SH_VOU + tristate "SuperH VOU video output driver" + depends on VIDEO_DEV && ARCH_SHMOBILE + select VIDEOBUF_DMA_CONTIG + help + Support for the Video Output Unit (VOU) on SuperH SoCs. + config CAPTURE_DAVINCI_DM646X_EVM tristate "DM646x EVM Video Capture" depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM @@ -542,7 +559,8 @@ config VIDEO_DAVINCI_VPIF config VIDEO_VIVI tristate "Virtual Video Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 + depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 && FONTS + select FONT_8x16 select VIDEOBUF_VMALLOC default n ---help--- @@ -613,6 +631,8 @@ config VIDEO_ISIF To compile this driver as a module, choose M here: the module will be called vpfe. +source "drivers/media/video/omap/Kconfig" + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS @@ -647,7 +667,7 @@ config VIDEO_CQCAM config VIDEO_W9966 tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux" - depends on PARPORT_1284 && PARPORT && VIDEO_V4L1 + depends on PARPORT_1284 && PARPORT && VIDEO_V4L2 help Video4linux driver for Winbond's w9966 based Webcams. Currently tested with the LifeView FlyCam Supra. @@ -740,7 +760,7 @@ source "drivers/media/video/zoran/Kconfig" config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on PCI && SONY_LAPTOP && VIDEO_V4L1 + depends on PCI && SONY_LAPTOP && VIDEO_V4L2 ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in @@ -807,7 +827,7 @@ source "drivers/media/video/saa7164/Kconfig" config VIDEO_M32R_AR tristate "AR devices" - depends on M32R && VIDEO_V4L1 + depends on M32R && VIDEO_V4L2 ---help--- This is a video4linux driver for the Renesas AR (Artificial Retina) camera module. @@ -1107,3 +1127,27 @@ config USB_S2255 endif # V4L_USB_DRIVERS endif # VIDEO_CAPTURE_DRIVERS + +menuconfig V4L_MEM2MEM_DRIVERS + bool "Memory-to-memory multimedia devices" + depends on VIDEO_V4L2 + default n + ---help--- + Say Y here to enable selecting drivers for V4L devices that + use system memory for both source and destination buffers, as opposed + to capture and output drivers, which use memory buffers for just + one of those. + +if V4L_MEM2MEM_DRIVERS + +config VIDEO_MEM2MEM_TESTDEV + tristate "Virtual test device for mem2mem framework" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF_VMALLOC + select V4L2_MEM2MEM_DEV + default n + ---help--- + This is a virtual test device for the memory-to-memory driver + framework. + +endif # V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index c51c386559f2..cc93859d3164 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -10,7 +10,8 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o +videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ + v4l2-event.o # V4L2 core modules @@ -117,6 +118,8 @@ obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o +obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o + obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o @@ -149,8 +152,11 @@ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o +obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ +obj-$(CONFIG_VIDEO_AK881X) += ak881x.o + obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o @@ -160,6 +166,10 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_ARCH_DAVINCI) += davinci/ + +obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o + obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ @@ -169,6 +179,8 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_ARCH_DAVINCI) += davinci/ +obj-$(CONFIG_ARCH_OMAP) += omap/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c new file mode 100644 index 000000000000..35390d4717b9 --- /dev/null +++ b/drivers/media/video/ak881x.c @@ -0,0 +1,368 @@ +/* + * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM) + * + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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 <linux/i2c.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/videodev2.h> + +#include <media/ak881x.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> + +#define AK881X_INTERFACE_MODE 0 +#define AK881X_VIDEO_PROCESS1 1 +#define AK881X_VIDEO_PROCESS2 2 +#define AK881X_VIDEO_PROCESS3 3 +#define AK881X_DAC_MODE 5 +#define AK881X_STATUS 0x24 +#define AK881X_DEVICE_ID 0x25 +#define AK881X_DEVICE_REVISION 0x26 + +struct ak881x { + struct v4l2_subdev subdev; + struct ak881x_pdata *pdata; + unsigned int lines; + int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */ + char revision; /* DEVICE_REVISION content */ +}; + +static int reg_read(struct i2c_client *client, const u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int reg_write(struct i2c_client *client, const u8 reg, + const u8 data) +{ + return i2c_smbus_write_byte_data(client, reg, data); +} + +static int reg_set(struct i2c_client *client, const u8 reg, + const u8 data, u8 mask) +{ + int ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static struct ak881x *to_ak881x(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ak881x, subdev); +} + +static int ak881x_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match.addr != client->addr) + return -ENODEV; + + id->ident = ak881x->id; + id->revision = ak881x->revision; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ak881x_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int ak881x_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + v4l_bound_align_image(&mf->width, 0, 720, 2, + &mf->height, 0, ak881x->lines, 1, 0); + mf->field = V4L2_FIELD_INTERLACED; + mf->code = V4L2_MBUS_FMT_YUYV8_2X8_LE; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + if (mf->field != V4L2_FIELD_INTERLACED || + mf->code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + return -EINVAL; + + return ak881x_try_g_mbus_fmt(sd, mf); +} + +static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + + *code = V4L2_MBUS_FMT_YUYV8_2X8_LE; + return 0; +} + +static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = 720; + a->bounds.height = ak881x->lines; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + u8 vp1; + + if (std == V4L2_STD_NTSC_443) { + vp1 = 3; + ak881x->lines = 480; + } else if (std == V4L2_STD_PAL_M) { + vp1 = 5; + ak881x->lines = 480; + } else if (std == V4L2_STD_PAL_60) { + vp1 = 7; + ak881x->lines = 480; + } else if (std && !(std & ~V4L2_STD_PAL)) { + vp1 = 0xf; + ak881x->lines = 576; + } else if (std && !(std & ~V4L2_STD_NTSC)) { + vp1 = 0; + ak881x->lines = 480; + } else { + /* No SECAM or PAL_N/Nc supported */ + return -EINVAL; + } + + reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf); + + return 0; +} + +static int ak881x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ak881x *ak881x = to_ak881x(client); + + if (enable) { + u8 dac; + /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */ + /* Default: composite output */ + if (ak881x->pdata->flags & AK881X_COMPONENT) + dac = 3; + else + dac = 4; + /* Turn on the DAC(s) */ + reg_write(client, AK881X_DAC_MODE, dac); + dev_dbg(&client->dev, "chip status 0x%x\n", + reg_read(client, AK881X_STATUS)); + } else { + /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */ + reg_write(client, AK881X_DAC_MODE, 0); + dev_dbg(&client->dev, "chip status 0x%x\n", + reg_read(client, AK881X_STATUS)); + } + + return 0; +} + +static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { + .g_chip_ident = ak881x_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ak881x_g_register, + .s_register = ak881x_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { + .s_mbus_fmt = ak881x_s_mbus_fmt, + .g_mbus_fmt = ak881x_try_g_mbus_fmt, + .try_mbus_fmt = ak881x_try_g_mbus_fmt, + .cropcap = ak881x_cropcap, + .enum_mbus_fmt = ak881x_enum_mbus_fmt, + .s_std_output = ak881x_s_std_output, + .s_stream = ak881x_s_stream, +}; + +static struct v4l2_subdev_ops ak881x_subdev_ops = { + .core = &ak881x_subdev_core_ops, + .video = &ak881x_subdev_video_ops, +}; + +static int ak881x_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct ak881x *ak881x; + u8 ifmode, data; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL); + if (!ak881x) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops); + + data = reg_read(client, AK881X_DEVICE_ID); + + switch (data) { + case 0x13: + ak881x->id = V4L2_IDENT_AK8813; + break; + case 0x14: + ak881x->id = V4L2_IDENT_AK8814; + break; + default: + dev_err(&client->dev, + "No ak881x chip detected, register read %x\n", data); + kfree(ak881x); + return -ENODEV; + } + + ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION); + ak881x->pdata = client->dev.platform_data; + + if (ak881x->pdata) { + if (ak881x->pdata->flags & AK881X_FIELD) + ifmode = 4; + else + ifmode = 0; + + switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) { + case AK881X_IF_MODE_BT656: + ifmode |= 1; + break; + case AK881X_IF_MODE_MASTER: + ifmode |= 2; + break; + case AK881X_IF_MODE_SLAVE: + default: + break; + } + + dev_dbg(&client->dev, "IF mode %x\n", ifmode); + + /* + * "Line Blanking No." seems to be the same as the number of + * "black" lines on, e.g., SuperH VOU, whose default value of 20 + * "incidentally" matches ak881x' default + */ + reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3)); + } + + /* Hardware default: NTSC-M */ + ak881x->lines = 480; + + dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n", + data, ak881x->revision); + + return 0; +} + +static int ak881x_remove(struct i2c_client *client) +{ + struct ak881x *ak881x = to_ak881x(client); + + v4l2_device_unregister_subdev(&ak881x->subdev); + kfree(ak881x); + + return 0; +} + +static const struct i2c_device_id ak881x_id[] = { + { "ak8813", 0 }, + { "ak8814", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak881x_id); + +static struct i2c_driver ak881x_i2c_driver = { + .driver = { + .name = "ak881x", + }, + .probe = ak881x_probe, + .remove = ak881x_remove, + .id_table = ak881x_id, +}; + +static int __init ak881x_module_init(void) +{ + return i2c_add_driver(&ak881x_i2c_driver); +} + +static void __exit ak881x_module_exit(void) +{ + i2c_del_driver(&ak881x_i2c_driver); +} + +module_init(ak881x_module_init); +module_exit(ak881x_module_exit); + +MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index a356d6bd3131..31e7a123d19a 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -27,8 +27,10 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/sched.h> -#include <linux/videodev.h> +#include <linux/version.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <linux/mutex.h> @@ -39,7 +41,7 @@ #include <asm/byteorder.h> #if 0 -#define DEBUG(n, args...) printk(args) +#define DEBUG(n, args...) printk(KERN_INFO args) #define CHECK_LOST 1 #else #define DEBUG(n, args...) @@ -52,10 +54,10 @@ */ #define USE_INT 0 /* Don't modify */ -#define VERSION "0.03" +#define VERSION "0.04" #define ar_inl(addr) inl((unsigned long)(addr)) -#define ar_outl(val, addr) outl((unsigned long)(val),(unsigned long)(addr)) +#define ar_outl(val, addr) outl((unsigned long)(val), (unsigned long)(addr)) extern struct cpuinfo_m32r boot_cpu_data; @@ -79,7 +81,7 @@ extern struct cpuinfo_m32r boot_cpu_data; /* bits & bytes per pixel */ #define AR_BITS_PER_PIXEL 16 -#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL/8) +#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL / 8) /* line buffer size */ #define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL) @@ -104,8 +106,9 @@ extern struct cpuinfo_m32r boot_cpu_data; #define AR_MODE_INTERLACE 0 #define AR_MODE_NORMAL 1 -struct ar_device { - struct video_device *vdev; +struct ar { + struct v4l2_device v4l2_dev; + struct video_device vdev; unsigned int start_capture; /* duaring capture in INT. mode. */ #if USE_INT unsigned char *line_buff; /* DMA line buffer */ @@ -116,12 +119,13 @@ struct ar_device { int width, height; int frame_bytes, line_bytes; wait_queue_head_t wait; - unsigned long in_use; struct mutex lock; }; +static struct ar ardev; + static int video_nr = -1; /* video device number (first free) */ -static unsigned char yuv[MAX_AR_FRAME_BYTES]; +static unsigned char yuv[MAX_AR_FRAME_BYTES]; /* module parameters */ /* default frequency */ @@ -133,9 +137,7 @@ module_param(freq, int, 0); module_param(vga, int, 0); module_param(vga_interlace, int, 0); -static int ar_initialize(struct video_device *dev); - -static inline void wait_for_vsync(void) +static void wait_for_vsync(void) { while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */ cpu_relax(); @@ -143,7 +145,7 @@ static inline void wait_for_vsync(void) cpu_relax(); } -static inline void wait_acknowledge(void) +static void wait_acknowledge(void) { int i; @@ -156,7 +158,7 @@ static inline void wait_acknowledge(void) /******************************************************************* * I2C functions *******************************************************************/ -void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, +static void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, unsigned long data3) { int i; @@ -200,7 +202,7 @@ void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, } -void init_iic(void) +static void init_iic(void) { DEBUG(1, "init_iic:\n"); @@ -214,13 +216,12 @@ void init_iic(void) /* I2C CLK */ /* 50MH-100k */ - if (freq == 75) { + if (freq == 75) ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */ - } else if (freq == 50) { + else if (freq == 50) ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */ - } else { + else ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */ - } ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */ } @@ -245,7 +246,7 @@ static inline void clear_dma_status(void) ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */ } -static inline void wait_for_vertical_sync(int exp_line) +static void wait_for_vertical_sync(struct ar *ar, int exp_line) { #if CHECK_LOST int tmout = 10000; /* FIXME */ @@ -260,7 +261,7 @@ static inline void wait_for_vertical_sync(int exp_line) break; } if (tmout < 0) - printk("arv: lost %d -> %d\n", exp_line, l); + v4l2_err(&ar->v4l2_dev, "lost %d -> %d\n", exp_line, l); #else while (ar_inl(ARVHCOUNT) != exp_line) cpu_relax(); @@ -269,15 +270,14 @@ static inline void wait_for_vertical_sync(int exp_line) static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - struct video_device *v = video_devdata(file); - struct ar_device *ar = video_get_drvdata(v); + struct ar *ar = video_drvdata(file); long ret = ar->frame_bytes; /* return read bytes */ unsigned long arvcr1 = 0; unsigned long flags; unsigned char *p; int h, w; unsigned char *py, *pu, *pv; -#if ! USE_INT +#if !USE_INT int l; #endif @@ -305,7 +305,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */ /* - * Okey , kicks AR LSI to invoke an interrupt + * Okay, kick AR LSI to invoke an interrupt */ ar->start_capture = 0; ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1); @@ -313,7 +313,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) /* .... AR interrupts .... */ interruptible_sleep_on(&ar->wait); if (signal_pending(current)) { - printk("arv: interrupted while get frame data.\n"); + printk(KERN_ERR "arv: interrupted while get frame data.\n"); ret = -EINTR; goto out_up; } @@ -334,7 +334,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) cpu_relax(); if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { for (h = 0; h < ar->height; h++) { - wait_for_vertical_sync(h); + wait_for_vertical_sync(ar, h); if (h < (AR_HEIGHT_VGA/2)) l = h << 1; else @@ -349,7 +349,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) } } else { for (h = 0; h < ar->height; h++) { - wait_for_vertical_sync(h); + wait_for_vertical_sync(ar, h); ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL); enable_dma(); while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) @@ -386,7 +386,7 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) } } if (copy_to_user(buf, yuv, ar->frame_bytes)) { - printk("arv: failed while copy_to_user yuv.\n"); + v4l2_err(&ar->v4l2_dev, "failed while copy_to_user yuv.\n"); ret = -EFAULT; goto out_up; } @@ -396,153 +396,127 @@ out_up: return ret; } -static long ar_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int ar_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) { - struct video_device *dev = video_devdata(file); - struct ar_device *ar = video_get_drvdata(dev); - - DEBUG(1, "ar_ioctl()\n"); - switch(cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - DEBUG(1, "VIDIOCGCAP:\n"); - strcpy(b->name, ar->vdev->name); - b->type = VID_TYPE_CAPTURE; - b->channels = 0; - b->audios = 0; - b->maxwidth = MAX_AR_WIDTH; - b->maxheight = MAX_AR_HEIGHT; - b->minwidth = MIN_AR_WIDTH; - b->minheight = MIN_AR_HEIGHT; - return 0; - } - case VIDIOCGCHAN: - DEBUG(1, "VIDIOCGCHAN:\n"); - return 0; - case VIDIOCSCHAN: - DEBUG(1, "VIDIOCSCHAN:\n"); - return 0; - case VIDIOCGTUNER: - DEBUG(1, "VIDIOCGTUNER:\n"); - return 0; - case VIDIOCSTUNER: - DEBUG(1, "VIDIOCSTUNER:\n"); - return 0; - case VIDIOCGPICT: - DEBUG(1, "VIDIOCGPICT:\n"); - return 0; - case VIDIOCSPICT: - DEBUG(1, "VIDIOCSPICT:\n"); - return 0; - case VIDIOCCAPTURE: - DEBUG(1, "VIDIOCCAPTURE:\n"); + struct ar *ar = video_drvdata(file); + + strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card)); + strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info)); + vcap->version = KERNEL_VERSION(0, 0, 4); + vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; +} + +static int ar_enum_input(struct file *file, void *fh, struct v4l2_input *vin) +{ + if (vin->index > 0) return -EINVAL; - case VIDIOCGWIN: - { - struct video_window *w = arg; - DEBUG(1, "VIDIOCGWIN:\n"); - memset(w, 0, sizeof(*w)); - w->width = ar->width; - w->height = ar->height; - return 0; + strlcpy(vin->name, "Camera", sizeof(vin->name)); + vin->type = V4L2_INPUT_TYPE_CAMERA; + vin->audioset = 0; + vin->tuner = 0; + vin->std = V4L2_STD_ALL; + vin->status = 0; + return 0; +} + +static int ar_g_input(struct file *file, void *fh, unsigned int *inp) +{ + *inp = 0; + return 0; +} + +static int ar_s_input(struct file *file, void *fh, unsigned int inp) +{ + return inp ? -EINVAL : 0; +} + +static int ar_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ar *ar = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + pix->width = ar->width; + pix->height = ar->height; + pix->pixelformat = V4L2_PIX_FMT_YUV422P; + pix->field = (ar->mode == AR_MODE_NORMAL) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED; + pix->bytesperline = ar->width; + pix->sizeimage = 2 * ar->width * ar->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int ar_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ar *ar = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + if (pix->height <= AR_HEIGHT_QVGA || pix->width <= AR_WIDTH_QVGA) { + pix->height = AR_HEIGHT_QVGA; + pix->width = AR_WIDTH_QVGA; + pix->field = V4L2_FIELD_INTERLACED; + } else { + pix->height = AR_HEIGHT_VGA; + pix->width = AR_WIDTH_VGA; + pix->field = vga_interlace ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; } - case VIDIOCSWIN: - { - struct video_window *w = arg; - DEBUG(1, "VIDIOCSWIN:\n"); - if ((w->width != AR_WIDTH_VGA || w->height != AR_HEIGHT_VGA) && - (w->width != AR_WIDTH_QVGA || w->height != AR_HEIGHT_QVGA)) - return -EINVAL; - - mutex_lock(&ar->lock); - ar->width = w->width; - ar->height = w->height; - if (ar->width == AR_WIDTH_VGA) { - ar->size = AR_SIZE_VGA; - ar->frame_bytes = AR_FRAME_BYTES_VGA; - ar->line_bytes = AR_LINE_BYTES_VGA; - if (vga_interlace) - ar->mode = AR_MODE_INTERLACE; - else - ar->mode = AR_MODE_NORMAL; - } else { - ar->size = AR_SIZE_QVGA; - ar->frame_bytes = AR_FRAME_BYTES_QVGA; - ar->line_bytes = AR_LINE_BYTES_QVGA; + pix->pixelformat = V4L2_PIX_FMT_YUV422P; + pix->bytesperline = ar->width; + pix->sizeimage = 2 * ar->width * ar->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int ar_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ar *ar = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int ret = ar_try_fmt_vid_cap(file, fh, fmt); + + if (ret) + return ret; + mutex_lock(&ar->lock); + ar->width = pix->width; + ar->height = pix->height; + if (ar->width == AR_WIDTH_VGA) { + ar->size = AR_SIZE_VGA; + ar->frame_bytes = AR_FRAME_BYTES_VGA; + ar->line_bytes = AR_LINE_BYTES_VGA; + if (vga_interlace) ar->mode = AR_MODE_INTERLACE; - } - mutex_unlock(&ar->lock); - return 0; - } - case VIDIOCGFBUF: - DEBUG(1, "VIDIOCGFBUF:\n"); - return -EINVAL; - case VIDIOCSFBUF: - DEBUG(1, "VIDIOCSFBUF:\n"); - return -EINVAL; - case VIDIOCKEY: - DEBUG(1, "VIDIOCKEY:\n"); - return 0; - case VIDIOCGFREQ: - DEBUG(1, "VIDIOCGFREQ:\n"); - return -EINVAL; - case VIDIOCSFREQ: - DEBUG(1, "VIDIOCSFREQ:\n"); - return -EINVAL; - case VIDIOCGAUDIO: - DEBUG(1, "VIDIOCGAUDIO:\n"); - return -EINVAL; - case VIDIOCSAUDIO: - DEBUG(1, "VIDIOCSAUDIO:\n"); - return -EINVAL; - case VIDIOCSYNC: - DEBUG(1, "VIDIOCSYNC:\n"); - return -EINVAL; - case VIDIOCMCAPTURE: - DEBUG(1, "VIDIOCMCAPTURE:\n"); - return -EINVAL; - case VIDIOCGMBUF: - DEBUG(1, "VIDIOCGMBUF:\n"); - return -EINVAL; - case VIDIOCGUNIT: - DEBUG(1, "VIDIOCGUNIT:\n"); - return -EINVAL; - case VIDIOCGCAPTURE: - DEBUG(1, "VIDIOCGCAPTURE:\n"); - return -EINVAL; - case VIDIOCSCAPTURE: - DEBUG(1, "VIDIOCSCAPTURE:\n"); - return -EINVAL; - case VIDIOCSPLAYMODE: - DEBUG(1, "VIDIOCSPLAYMODE:\n"); - return -EINVAL; - case VIDIOCSWRITEMODE: - DEBUG(1, "VIDIOCSWRITEMODE:\n"); - return -EINVAL; - case VIDIOCGPLAYINFO: - DEBUG(1, "VIDIOCGPLAYINFO:\n"); - return -EINVAL; - case VIDIOCSMICROCODE: - DEBUG(1, "VIDIOCSMICROCODE:\n"); - return -EINVAL; - case VIDIOCGVBIFMT: - DEBUG(1, "VIDIOCGVBIFMT:\n"); - return -EINVAL; - case VIDIOCSVBIFMT: - DEBUG(1, "VIDIOCSVBIFMT:\n"); - return -EINVAL; - default: - DEBUG(1, "Unknown ioctl(0x%08x)\n", cmd); - return -ENOIOCTLCMD; + else + ar->mode = AR_MODE_NORMAL; + } else { + ar->size = AR_SIZE_QVGA; + ar->frame_bytes = AR_FRAME_BYTES_QVGA; + ar->line_bytes = AR_LINE_BYTES_QVGA; + ar->mode = AR_MODE_INTERLACE; } + /* Ok we figured out what to use from our wide choice */ + mutex_unlock(&ar->lock); return 0; } -static long ar_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static int ar_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { - return video_usercopy(file, cmd, arg, ar_do_ioctl); + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "YUV 4:2:2 Planar", V4L2_PIX_FMT_YUV422P, + { 0, 0, 0, 0 } + }, + }; + enum v4l2_buf_type type = fmt->type; + + if (fmt->index > 0) + return -EINVAL; + + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; } #if USE_INT @@ -551,7 +525,7 @@ static long ar_ioctl(struct file *file, unsigned int cmd, */ static void ar_interrupt(int irq, void *dev) { - struct ar_device *ar = dev; + struct ar *ar = dev; unsigned int line_count; unsigned int line_number; unsigned int arvcr1; @@ -559,11 +533,11 @@ static void ar_interrupt(int irq, void *dev) line_count = ar_inl(ARVHCOUNT); /* line number */ if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { /* operations for interlace mode */ - if ( line_count < (AR_HEIGHT_VGA/2) ) /* even line */ + if (line_count < (AR_HEIGHT_VGA / 2)) /* even line */ line_number = (line_count << 1); else /* odd line */ line_number = - (((line_count - (AR_HEIGHT_VGA/2)) << 1) + 1); + (((line_count - (AR_HEIGHT_VGA / 2)) << 1) + 1); } else { line_number = line_count; } @@ -623,11 +597,10 @@ static void ar_interrupt(int irq, void *dev) * 0 is returned in success. * */ -static int ar_initialize(struct video_device *dev) +static int ar_initialize(struct ar *ar) { - struct ar_device *ar = video_get_drvdata(dev); unsigned long cr = 0; - int i,found=0; + int i, found = 0; DEBUG(1, "ar_initialize:\n"); @@ -666,129 +639,119 @@ static int ar_initialize(struct video_device *dev) if (found == 0) return -ENODEV; - printk("arv: Initializing "); - - iic(2,0x78,0x11,0x01,0x00); /* start */ - iic(3,0x78,0x12,0x00,0x06); - iic(3,0x78,0x12,0x12,0x30); - iic(3,0x78,0x12,0x15,0x58); - iic(3,0x78,0x12,0x17,0x30); - printk("."); - iic(3,0x78,0x12,0x1a,0x97); - iic(3,0x78,0x12,0x1b,0xff); - iic(3,0x78,0x12,0x1c,0xff); - iic(3,0x78,0x12,0x26,0x10); - iic(3,0x78,0x12,0x27,0x00); - printk("."); - iic(2,0x78,0x34,0x02,0x00); - iic(2,0x78,0x7a,0x10,0x00); - iic(2,0x78,0x80,0x39,0x00); - iic(2,0x78,0x81,0xe6,0x00); - iic(2,0x78,0x8d,0x00,0x00); - printk("."); - iic(2,0x78,0x8e,0x0c,0x00); - iic(2,0x78,0x8f,0x00,0x00); + v4l2_info(&ar->v4l2_dev, "Initializing "); + + iic(2, 0x78, 0x11, 0x01, 0x00); /* start */ + iic(3, 0x78, 0x12, 0x00, 0x06); + iic(3, 0x78, 0x12, 0x12, 0x30); + iic(3, 0x78, 0x12, 0x15, 0x58); + iic(3, 0x78, 0x12, 0x17, 0x30); + printk(KERN_CONT "."); + iic(3, 0x78, 0x12, 0x1a, 0x97); + iic(3, 0x78, 0x12, 0x1b, 0xff); + iic(3, 0x78, 0x12, 0x1c, 0xff); + iic(3, 0x78, 0x12, 0x26, 0x10); + iic(3, 0x78, 0x12, 0x27, 0x00); + printk(KERN_CONT "."); + iic(2, 0x78, 0x34, 0x02, 0x00); + iic(2, 0x78, 0x7a, 0x10, 0x00); + iic(2, 0x78, 0x80, 0x39, 0x00); + iic(2, 0x78, 0x81, 0xe6, 0x00); + iic(2, 0x78, 0x8d, 0x00, 0x00); + printk(KERN_CONT "."); + iic(2, 0x78, 0x8e, 0x0c, 0x00); + iic(2, 0x78, 0x8f, 0x00, 0x00); #if 0 - iic(2,0x78,0x90,0x00,0x00); /* AWB on=1 off=0 */ + iic(2, 0x78, 0x90, 0x00, 0x00); /* AWB on=1 off=0 */ #endif - iic(2,0x78,0x93,0x01,0x00); - iic(2,0x78,0x94,0xcd,0x00); - iic(2,0x78,0x95,0x00,0x00); - printk("."); - iic(2,0x78,0x96,0xa0,0x00); - iic(2,0x78,0x97,0x00,0x00); - iic(2,0x78,0x98,0x60,0x00); - iic(2,0x78,0x99,0x01,0x00); - iic(2,0x78,0x9a,0x19,0x00); - printk("."); - iic(2,0x78,0x9b,0x02,0x00); - iic(2,0x78,0x9c,0xe8,0x00); - iic(2,0x78,0x9d,0x02,0x00); - iic(2,0x78,0x9e,0x2e,0x00); - iic(2,0x78,0xb8,0x78,0x00); - iic(2,0x78,0xba,0x05,0x00); + iic(2, 0x78, 0x93, 0x01, 0x00); + iic(2, 0x78, 0x94, 0xcd, 0x00); + iic(2, 0x78, 0x95, 0x00, 0x00); + printk(KERN_CONT "."); + iic(2, 0x78, 0x96, 0xa0, 0x00); + iic(2, 0x78, 0x97, 0x00, 0x00); + iic(2, 0x78, 0x98, 0x60, 0x00); + iic(2, 0x78, 0x99, 0x01, 0x00); + iic(2, 0x78, 0x9a, 0x19, 0x00); + printk(KERN_CONT "."); + iic(2, 0x78, 0x9b, 0x02, 0x00); + iic(2, 0x78, 0x9c, 0xe8, 0x00); + iic(2, 0x78, 0x9d, 0x02, 0x00); + iic(2, 0x78, 0x9e, 0x2e, 0x00); + iic(2, 0x78, 0xb8, 0x78, 0x00); + iic(2, 0x78, 0xba, 0x05, 0x00); #if 0 - iic(2,0x78,0x83,0x8c,0x00); /* brightness */ + iic(2, 0x78, 0x83, 0x8c, 0x00); /* brightness */ #endif - printk("."); + printk(KERN_CONT "."); /* color correction */ - iic(3,0x78,0x49,0x00,0x95); /* a */ - iic(3,0x78,0x49,0x01,0x96); /* b */ - iic(3,0x78,0x49,0x03,0x85); /* c */ - iic(3,0x78,0x49,0x04,0x97); /* d */ - iic(3,0x78,0x49,0x02,0x7e); /* e(Lo) */ - iic(3,0x78,0x49,0x05,0xa4); /* f(Lo) */ - iic(3,0x78,0x49,0x06,0x04); /* e(Hi) */ - iic(3,0x78,0x49,0x07,0x04); /* e(Hi) */ - iic(2,0x78,0x48,0x01,0x00); /* on=1 off=0 */ - - printk("."); - iic(2,0x78,0x11,0x00,0x00); /* end */ - printk(" done\n"); + iic(3, 0x78, 0x49, 0x00, 0x95); /* a */ + iic(3, 0x78, 0x49, 0x01, 0x96); /* b */ + iic(3, 0x78, 0x49, 0x03, 0x85); /* c */ + iic(3, 0x78, 0x49, 0x04, 0x97); /* d */ + iic(3, 0x78, 0x49, 0x02, 0x7e); /* e(Lo) */ + iic(3, 0x78, 0x49, 0x05, 0xa4); /* f(Lo) */ + iic(3, 0x78, 0x49, 0x06, 0x04); /* e(Hi) */ + iic(3, 0x78, 0x49, 0x07, 0x04); /* e(Hi) */ + iic(2, 0x78, 0x48, 0x01, 0x00); /* on=1 off=0 */ + + printk(KERN_CONT "."); + iic(2, 0x78, 0x11, 0x00, 0x00); /* end */ + printk(KERN_CONT " done\n"); return 0; } -void ar_release(struct video_device *vfd) -{ - struct ar_device *ar = video_get_drvdata(vfd); - mutex_lock(&ar->lock); - video_device_release(vfd); -} - /**************************************************************************** * * Video4Linux Module functions * ****************************************************************************/ -static struct ar_device ardev; - -static int ar_exclusive_open(struct file *file) -{ - return test_and_set_bit(0, &ardev.in_use) ? -EBUSY : 0; -} - -static int ar_exclusive_release(struct file *file) -{ - clear_bit(0, &ardev.in_use); - return 0; -} static const struct v4l2_file_operations ar_fops = { .owner = THIS_MODULE, - .open = ar_exclusive_open, - .release = ar_exclusive_release, .read = ar_read, - .ioctl = ar_ioctl, + .ioctl = video_ioctl2, }; -static struct video_device ar_template = { - .name = "Colour AR VGA", - .fops = &ar_fops, - .release = ar_release, +static const struct v4l2_ioctl_ops ar_ioctl_ops = { + .vidioc_querycap = ar_querycap, + .vidioc_g_input = ar_g_input, + .vidioc_s_input = ar_s_input, + .vidioc_enum_input = ar_enum_input, + .vidioc_enum_fmt_vid_cap = ar_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = ar_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = ar_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = ar_try_fmt_vid_cap, }; #define ALIGN4(x) ((((int)(x)) & 0x3) == 0) static int __init ar_init(void) { - struct ar_device *ar; + struct ar *ar; + struct v4l2_device *v4l2_dev; int ret; int i; - DEBUG(1, "ar_init:\n"); - ret = -EIO; - printk(KERN_INFO "arv: Colour AR VGA driver %s\n", VERSION); - ar = &ardev; - memset(ar, 0, sizeof(struct ar_device)); + v4l2_dev = &ar->v4l2_dev; + strlcpy(v4l2_dev->name, "arv", sizeof(v4l2_dev->name)); + v4l2_info(v4l2_dev, "Colour AR VGA driver %s\n", VERSION); + + ret = v4l2_device_register(NULL, v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return ret; + } + ret = -EIO; #if USE_INT /* allocate a DMA buffer for 1 line. */ ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA); - if (ar->line_buff == NULL || ! ALIGN4(ar->line_buff)) { - printk("arv: buffer allocation failed for DMA.\n"); + if (ar->line_buff == NULL || !ALIGN4(ar->line_buff)) { + v4l2_err(v4l2_dev, "buffer allocation failed for DMA.\n"); ret = -ENOMEM; goto out_end; } @@ -796,20 +759,19 @@ static int __init ar_init(void) /* allocate buffers for a frame */ for (i = 0; i < MAX_AR_HEIGHT; i++) { ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL); - if (ar->frame[i] == NULL || ! ALIGN4(ar->frame[i])) { - printk("arv: buffer allocation failed for frame.\n"); + if (ar->frame[i] == NULL || !ALIGN4(ar->frame[i])) { + v4l2_err(v4l2_dev, "buffer allocation failed for frame.\n"); ret = -ENOMEM; goto out_line_buff; } } - ar->vdev = video_device_alloc(); - if (!ar->vdev) { - printk(KERN_ERR "arv: video_device_alloc() failed\n"); - return -ENOMEM; - } - memcpy(ar->vdev, &ar_template, sizeof(ar_template)); - video_set_drvdata(ar->vdev, ar); + strlcpy(ar->vdev.name, "Colour AR VGA", sizeof(ar->vdev.name)); + ar->vdev.v4l2_dev = v4l2_dev; + ar->vdev.fops = &ar_fops; + ar->vdev.ioctl_ops = &ar_ioctl_ops; + ar->vdev.release = video_device_release_empty; + video_set_drvdata(&ar->vdev, ar); if (vga) { ar->width = AR_WIDTH_VGA; @@ -834,14 +796,14 @@ static int __init ar_init(void) #if USE_INT if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) { - printk("arv: request_irq(%d) failed.\n", M32R_IRQ_INT3); + v4l2_err("request_irq(%d) failed.\n", M32R_IRQ_INT3); ret = -EIO; goto out_irq; } #endif - if (ar_initialize(ar->vdev) != 0) { - printk("arv: M64278 not found.\n"); + if (ar_initialize(ar) != 0) { + v4l2_err(v4l2_dev, "M64278 not found.\n"); ret = -ENODEV; goto out_dev; } @@ -852,15 +814,15 @@ static int __init ar_init(void) * device is named "video[0-64]". * video_register_device() initializes h/w using ar_initialize(). */ - if (video_register_device(ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) { + if (video_register_device(&ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) { /* return -1, -ENFILE(full) or others */ - printk("arv: register video (Colour AR) failed.\n"); + v4l2_err(v4l2_dev, "register video (Colour AR) failed.\n"); ret = -ENODEV; goto out_dev; } - printk("%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n", - video_device_node_name(ar->vdev), M32R_IRQ_INT3, freq); + v4l2_info(v4l2_dev, "%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n", + video_device_node_name(&ar->vdev), M32R_IRQ_INT3, freq); return 0; @@ -879,6 +841,7 @@ out_line_buff: out_end: #endif + v4l2_device_unregister(&ar->v4l2_dev); return ret; } @@ -886,7 +849,7 @@ out_end: static int __init ar_init_module(void) { freq = (boot_cpu_data.bus_clock / 1000000); - printk("arv: Bus clock %d\n", freq); + printk(KERN_INFO "arv: Bus clock %d\n", freq); if (freq != 50 && freq != 75) freq = DEFAULT_FREQ; return ar_init(); @@ -894,11 +857,11 @@ static int __init ar_init_module(void) static void __exit ar_cleanup_module(void) { - struct ar_device *ar; + struct ar *ar; int i; ar = &ardev; - video_unregister_device(ar->vdev); + video_unregister_device(&ar->vdev); #if USE_INT free_irq(M32R_IRQ_INT3, ar); #endif @@ -907,6 +870,7 @@ static void __exit ar_cleanup_module(void) #if USE_INT kfree(ar->line_buff); #endif + v4l2_device_unregister(&ar->v4l2_dev); } module_init(ar_init_module); diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 8c140c01c5e6..66150216f976 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -1105,7 +1105,7 @@ static int vidioc_enum_input(struct file *file, void *priv, tmp = input->index; - if (tmp > AU0828_MAX_INPUT) + if (tmp >= AU0828_MAX_INPUT) return -EINVAL; if (AUVI_INPUT(tmp).type == 0) return -EINVAL; diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 716870ae85d5..7af56cde0c79 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -241,6 +241,10 @@ static struct CARD { { 0xa1550101, BTTV_BOARD_IVC200, "IVC-200G" }, { 0xa1550102, BTTV_BOARD_IVC200, "IVC-200G" }, { 0xa1550103, BTTV_BOARD_IVC200, "IVC-200G" }, + { 0xa1550800, BTTV_BOARD_IVC200, "IVC-200" }, + { 0xa1550801, BTTV_BOARD_IVC200, "IVC-200" }, + { 0xa1550802, BTTV_BOARD_IVC200, "IVC-200" }, + { 0xa1550803, BTTV_BOARD_IVC200, "IVC-200" }, { 0xa182ff00, BTTV_BOARD_IVC120, "IVC-120G" }, { 0xa182ff01, BTTV_BOARD_IVC120, "IVC-120G" }, { 0xa182ff02, BTTV_BOARD_IVC120, "IVC-120G" }, diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index f4860f03dfc3..38c7f78ad9cf 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -1525,7 +1525,7 @@ static int bttv_s_ctrl(struct file *file, void *f, struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -1806,8 +1806,8 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) *size = fh->fmt->depth*fh->width*fh->height >> 3; if (0 == *count) *count = gbuffers; - while (*size * *count > gbuffers * gbufsize) - (*count)--; + if (*size * *count > gbuffers * gbufsize) + *count = (gbuffers * gbufsize) / *size; return 0; } @@ -1859,7 +1859,7 @@ static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id) unsigned int i; int err; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -1941,7 +1941,7 @@ static int bttv_s_input(struct file *file, void *priv, unsigned int i) int err; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -1961,7 +1961,7 @@ static int bttv_s_tuner(struct file *file, void *priv, struct bttv *btv = fh->btv; int err; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -1987,11 +1987,6 @@ static int bttv_g_frequency(struct file *file, void *priv, { struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - int err; - - err = v4l2_prio_check(&btv->prio, &fh->prio); - if (0 != err) - return err; f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = btv->freq; @@ -2006,7 +2001,7 @@ static int bttv_s_frequency(struct file *file, void *priv, struct bttv *btv = fh->btv; int err; - err = v4l2_prio_check(&btv->prio, &fh->prio); + err = v4l2_prio_check(&btv->prio, fh->prio); if (0 != err) return err; @@ -3029,7 +3024,7 @@ static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop) crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - retval = v4l2_prio_check(&btv->prio, &fh->prio); + retval = v4l2_prio_check(&btv->prio, fh->prio); if (0 != retval) return retval; @@ -3241,7 +3236,7 @@ static int bttv_open(struct file *file) *fh = btv->init; fh->type = type; fh->ov.setup_ok = 0; - v4l2_prio_open(&btv->prio,&fh->prio); + v4l2_prio_open(&btv->prio, &fh->prio); videobuf_queue_sg_init(&fh->cap, &bttv_video_qops, &btv->c.pci->dev, &btv->s_lock, @@ -3312,7 +3307,7 @@ static int bttv_release(struct file *file) /* free stuff */ videobuf_mmap_free(&fh->cap); videobuf_mmap_free(&fh->vbi); - v4l2_prio_close(&btv->prio,&fh->prio); + v4l2_prio_close(&btv->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -3449,7 +3444,7 @@ static int radio_release(struct file *file) struct bttv *btv = fh->btv; struct rds_command cmd; - v4l2_prio_close(&btv->prio,&fh->prio); + v4l2_prio_close(&btv->prio, fh->prio); file->private_data = NULL; kfree(fh); diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index aa153a986ade..f68717a4bdec 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -49,6 +49,8 @@ module_param(ir_rc5_key_timeout, int, 0644); #define DEVNAME "bttv-input" +#define MODULE_NAME "bttv" + /* ---------------------------------------------------------------------- */ static void ir_handle_key(struct bttv *btv) @@ -246,7 +248,7 @@ static void bttv_ir_stop(struct bttv *btv) int bttv_input_init(struct bttv *btv) { struct card_ir *ir; - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; struct input_dev *input_dev; u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; @@ -264,7 +266,7 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_AVERMEDIA: case BTTV_BOARD_AVPHONE98: case BTTV_BOARD_AVERMEDIA98: - ir_codes = &ir_codes_avermedia_table; + ir_codes = RC_MAP_AVERMEDIA; ir->mask_keycode = 0xf88000; ir->mask_keydown = 0x010000; ir->polling = 50; // ms @@ -272,14 +274,14 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_AVDVBT_761: case BTTV_BOARD_AVDVBT_771: - ir_codes = &ir_codes_avermedia_dvbt_table; + ir_codes = RC_MAP_AVERMEDIA_DVBT; ir->mask_keycode = 0x0f00c0; ir->mask_keydown = 0x000020; ir->polling = 50; // ms break; case BTTV_BOARD_PXELVWPLTVPAK: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; ir->mask_keycode = 0x003e00; ir->mask_keyup = 0x010000; ir->polling = 50; // ms @@ -287,24 +289,24 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_PV_M4900: case BTTV_BOARD_PV_BT878P_9B: case BTTV_BOARD_PV_BT878P_PLUS: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x008000; ir->polling = 50; // ms break; case BTTV_BOARD_WINFAST2000: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; ir->mask_keycode = 0x1f8; break; case BTTV_BOARD_MAGICTVIEW061: case BTTV_BOARD_MAGICTVIEW063: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; ir->mask_keycode = 0x0008e000; ir->mask_keydown = 0x00200000; break; case BTTV_BOARD_APAC_VIEWCOMP: - ir_codes = &ir_codes_apac_viewcomp_table; + ir_codes = RC_MAP_APAC_VIEWCOMP; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x008000; ir->polling = 50; // ms @@ -312,30 +314,30 @@ int bttv_input_init(struct bttv *btv) case BTTV_BOARD_ASKEY_CPH03X: case BTTV_BOARD_CONCEPTRONIC_CTVFMI2: case BTTV_BOARD_CONTVFMI: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; ir->mask_keycode = 0x001F00; ir->mask_keyup = 0x006000; ir->polling = 50; // ms break; case BTTV_BOARD_NEBULA_DIGITV: - ir_codes = &ir_codes_nebula_table; + ir_codes = RC_MAP_NEBULA; btv->custom_irq = bttv_rc5_irq; ir->rc5_gpio = 1; break; case BTTV_BOARD_MACHTV_MAGICTV: - ir_codes = &ir_codes_apac_viewcomp_table; + ir_codes = RC_MAP_APAC_VIEWCOMP; ir->mask_keycode = 0x001F00; ir->mask_keyup = 0x004000; ir->polling = 50; /* ms */ break; case BTTV_BOARD_KOZUMI_KTV_01C: - ir_codes = &ir_codes_pctv_sedna_table; + ir_codes = RC_MAP_PCTV_SEDNA; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x006000; ir->polling = 50; /* ms */ break; case BTTV_BOARD_ENLTV_FM_2: - ir_codes = &ir_codes_encore_enltv2_table; + ir_codes = RC_MAP_ENCORE_ENLTV2; ir->mask_keycode = 0x00fd00; ir->mask_keyup = 0x000080; ir->polling = 1; /* ms */ @@ -390,7 +392,7 @@ int bttv_input_init(struct bttv *btv) bttv_ir_start(btv, ir); /* all done */ - err = ir_input_register(btv->remote->dev, ir_codes, NULL); + err = ir_input_register(btv->remote->dev, ir_codes, NULL, MODULE_NAME); if (err) goto err_out_stop; diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index 9e39bc5f7b00..3c9e754d73a0 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -80,8 +80,8 @@ OTHER DEALINGS IN THE SOFTWARE. #include "bw-qcam.h" -static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */ -static unsigned int yieldlines=4; /* Yield after this many during capture */ +static unsigned int maxpoll = 250; /* Maximum busy-loop count for qcam I/O */ +static unsigned int yieldlines = 4; /* Yield after this many during capture */ static int video_nr = -1; static unsigned int force_init; /* Whether to probe aggressively */ @@ -156,7 +156,7 @@ static int qc_calibrate(struct qcam_device *q) mdelay(1); schedule(); count++; - } while (value == 0xff && count<2048); + } while (value == 0xff && count < 2048); q->whitebal = value; return value; @@ -170,16 +170,15 @@ static struct qcam_device *qcam_init(struct parport *port) struct qcam_device *q; q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); - if(q==NULL) + if (q == NULL) return NULL; q->pport = port; q->pdev = parport_register_device(port, "bw-qcam", NULL, NULL, - NULL, 0, NULL); - if (q->pdev == NULL) - { + NULL, 0, NULL); + if (q->pdev == NULL) { printk(KERN_ERR "bw-qcam: couldn't register for %s.\n", - port->name); + port->name); kfree(q); return NULL; } @@ -247,12 +246,10 @@ static int qc_readparam(struct qcam_device *q) static int qc_waithand(struct qcam_device *q, int val) { int status; - int runs=0; + int runs = 0; - if (val) - { - while (!((status = read_lpstatus(q)) & 8)) - { + if (val) { + while (!((status = read_lpstatus(q)) & 8)) { /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly until the camera wakes up. However, we are @@ -260,18 +257,13 @@ static int qc_waithand(struct qcam_device *q, int val) setting it lower is much better for interactive response. */ - if(runs++>maxpoll) - { + if (runs++ > maxpoll) msleep_interruptible(5); - } - if(runs>(maxpoll+1000)) /* 5 seconds */ + if (runs > (maxpoll + 1000)) /* 5 seconds */ return -1; } - } - else - { - while (((status = read_lpstatus(q)) & 8)) - { + } else { + while (((status = read_lpstatus(q)) & 8)) { /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly until the camera wakes up. However, we are @@ -279,11 +271,9 @@ static int qc_waithand(struct qcam_device *q, int val) setting it lower is much better for interactive response. */ - if(runs++>maxpoll) - { + if (runs++ > maxpoll) msleep_interruptible(5); - } - if(runs++>(maxpoll+1000)) /* 5 seconds */ + if (runs++ > (maxpoll + 1000)) /* 5 seconds */ return -1; } } @@ -299,10 +289,9 @@ static int qc_waithand(struct qcam_device *q, int val) static unsigned int qc_waithand2(struct qcam_device *q, int val) { unsigned int status; - int runs=0; + int runs = 0; - do - { + do { status = read_lpdata(q); /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly @@ -311,14 +300,11 @@ static unsigned int qc_waithand2(struct qcam_device *q, int val) setting it lower is much better for interactive response. */ - if(runs++>maxpoll) - { + if (runs++ > maxpoll) msleep_interruptible(5); - } - if(runs++>(maxpoll+1000)) /* 5 seconds */ + if (runs++ > (maxpoll + 1000)) /* 5 seconds */ return 0; - } - while ((status & 1) != val); + } while ((status & 1) != val); return status; } @@ -342,8 +328,7 @@ static int qc_detect(struct qcam_device *q) lastreg = reg = read_lpstatus(q) & 0xf0; - for (i = 0; i < 500; i++) - { + for (i = 0; i < 500; i++) { reg = read_lpstatus(q) & 0xf0; if (reg != lastreg) count++; @@ -357,7 +342,7 @@ static int qc_detect(struct qcam_device *q) won't be flashing these bits. Possibly unloading the module in the middle of a grab? Or some timeout condition? I've seen this parameter as low as 19 on my 450Mhz box - mpc */ - printk("Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); + printk(KERN_DEBUG "Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); return 1; #endif @@ -367,7 +352,7 @@ static int qc_detect(struct qcam_device *q) return 1; /* found */ } else { printk(KERN_ERR "No Quickcam found on port %s\n", - q->pport->name); + q->pport->name); printk(KERN_DEBUG "Quickcam detection counter: %u\n", count); return 0; /* not found */ } @@ -381,26 +366,24 @@ static int qc_detect(struct qcam_device *q) static void qc_reset(struct qcam_device *q) { - switch (q->port_mode & QC_FORCE_MASK) - { - case QC_FORCE_UNIDIR: - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; - break; + switch (q->port_mode & QC_FORCE_MASK) { + case QC_FORCE_UNIDIR: + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; + break; - case QC_FORCE_BIDIR: - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; - break; + case QC_FORCE_BIDIR: + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; + break; - case QC_ANY: - write_lpcontrol(q, 0x20); - write_lpdata(q, 0x75); + case QC_ANY: + write_lpcontrol(q, 0x20); + write_lpdata(q, 0x75); - if (read_lpdata(q) != 0x75) { - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; - } else { - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; - } - break; + if (read_lpdata(q) != 0x75) + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; + else + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; + break; } write_lpcontrol(q, 0xb); @@ -423,36 +406,33 @@ static int qc_setscanmode(struct qcam_device *q) { int old_mode = q->mode; - switch (q->transfer_scale) - { - case 1: - q->mode = 0; - break; - case 2: - q->mode = 4; - break; - case 4: - q->mode = 8; - break; + switch (q->transfer_scale) { + case 1: + q->mode = 0; + break; + case 2: + q->mode = 4; + break; + case 4: + q->mode = 8; + break; } - switch (q->bpp) - { - case 4: - break; - case 6: - q->mode += 2; - break; + switch (q->bpp) { + case 4: + break; + case 6: + q->mode += 2; + break; } - switch (q->port_mode & QC_MODE_MASK) - { - case QC_BIDIR: - q->mode += 1; - break; - case QC_NOTSET: - case QC_UNIDIR: - break; + switch (q->port_mode & QC_MODE_MASK) { + case QC_BIDIR: + q->mode += 1; + break; + case QC_NOTSET: + case QC_UNIDIR: + break; } if (q->mode != old_mode) @@ -493,7 +473,7 @@ static void qc_set(struct qcam_device *q) } else { val = q->width * q->bpp; val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * - q->transfer_scale; + q->transfer_scale; } val = DIV_ROUND_UP(val, val2); qc_command(q, 0x13); @@ -521,85 +501,80 @@ static void qc_set(struct qcam_device *q) static inline int qc_readbytes(struct qcam_device *q, char buffer[]) { - int ret=1; + int ret = 1; unsigned int hi, lo; unsigned int hi2, lo2; static int state; - if (buffer == NULL) - { + if (buffer == NULL) { state = 0; return 0; } - switch (q->port_mode & QC_MODE_MASK) - { - case QC_BIDIR: /* Bi-directional Port */ - write_lpcontrol(q, 0x26); - lo = (qc_waithand2(q, 1) >> 1); - hi = (read_lpstatus(q) >> 3) & 0x1f; - write_lpcontrol(q, 0x2e); - lo2 = (qc_waithand2(q, 0) >> 1); - hi2 = (read_lpstatus(q) >> 3) & 0x1f; - switch (q->bpp) - { - case 4: - buffer[0] = lo & 0xf; - buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3); - buffer[2] = (hi & 0x1e) >> 1; - buffer[3] = lo2 & 0xf; - buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3); - buffer[5] = (hi2 & 0x1e) >> 1; - ret = 6; - break; - case 6: - buffer[0] = lo & 0x3f; - buffer[1] = ((lo & 0x40) >> 6) | (hi << 1); - buffer[2] = lo2 & 0x3f; - buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1); - ret = 4; - break; - } + switch (q->port_mode & QC_MODE_MASK) { + case QC_BIDIR: /* Bi-directional Port */ + write_lpcontrol(q, 0x26); + lo = (qc_waithand2(q, 1) >> 1); + hi = (read_lpstatus(q) >> 3) & 0x1f; + write_lpcontrol(q, 0x2e); + lo2 = (qc_waithand2(q, 0) >> 1); + hi2 = (read_lpstatus(q) >> 3) & 0x1f; + switch (q->bpp) { + case 4: + buffer[0] = lo & 0xf; + buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3); + buffer[2] = (hi & 0x1e) >> 1; + buffer[3] = lo2 & 0xf; + buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3); + buffer[5] = (hi2 & 0x1e) >> 1; + ret = 6; + break; + case 6: + buffer[0] = lo & 0x3f; + buffer[1] = ((lo & 0x40) >> 6) | (hi << 1); + buffer[2] = lo2 & 0x3f; + buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1); + ret = 4; break; + } + break; + + case QC_UNIDIR: /* Unidirectional Port */ + write_lpcontrol(q, 6); + lo = (qc_waithand(q, 1) & 0xf0) >> 4; + write_lpcontrol(q, 0xe); + hi = (qc_waithand(q, 0) & 0xf0) >> 4; - case QC_UNIDIR: /* Unidirectional Port */ - write_lpcontrol(q, 6); - lo = (qc_waithand(q, 1) & 0xf0) >> 4; - write_lpcontrol(q, 0xe); - hi = (qc_waithand(q, 0) & 0xf0) >> 4; - - switch (q->bpp) - { - case 4: - buffer[0] = lo; - buffer[1] = hi; - ret = 2; - break; - case 6: - switch (state) - { - case 0: - buffer[0] = (lo << 2) | ((hi & 0xc) >> 2); - q->saved_bits = (hi & 3) << 4; - state = 1; - ret = 1; - break; - case 1: - buffer[0] = lo | q->saved_bits; - q->saved_bits = hi << 2; - state = 2; - ret = 1; - break; - case 2: - buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits; - buffer[1] = ((lo & 3) << 4) | hi; - state = 0; - ret = 2; - break; - } - break; + switch (q->bpp) { + case 4: + buffer[0] = lo; + buffer[1] = hi; + ret = 2; + break; + case 6: + switch (state) { + case 0: + buffer[0] = (lo << 2) | ((hi & 0xc) >> 2); + q->saved_bits = (hi & 3) << 4; + state = 1; + ret = 1; + break; + case 1: + buffer[0] = lo | q->saved_bits; + q->saved_bits = hi << 2; + state = 2; + ret = 1; + break; + case 2: + buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits; + buffer[1] = ((lo & 3) << 4) | hi; + state = 0; + ret = 2; + break; } break; + } + break; } return ret; } @@ -615,7 +590,7 @@ static inline int qc_readbytes(struct qcam_device *q, char buffer[]) * n=2^(bit depth)-1. Ask me for more details if you don't understand * this. */ -static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long len) +static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long len) { int i, j, k, yield; int bytes; @@ -623,9 +598,9 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l int divisor; int pixels_per_line; int pixels_read = 0; - int got=0; + int got = 0; char buffer[6]; - int shift=8-q->bpp; + int shift = 8 - q->bpp; char invert; if (q->mode == -1) @@ -634,13 +609,12 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l qc_command(q, 0x7); qc_command(q, q->mode); - if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) - { + if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) { write_lpcontrol(q, 0x2e); /* turn port around */ write_lpcontrol(q, 0x26); - (void) qc_waithand(q, 1); + qc_waithand(q, 1); write_lpcontrol(q, 0x2e); - (void) qc_waithand(q, 0); + qc_waithand(q, 0); } /* strange -- should be 15:63 below, but 4bpp is odd */ @@ -650,33 +624,28 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l pixels_per_line = q->width / q->transfer_scale; transperline = q->width * q->bpp; divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * - q->transfer_scale; + q->transfer_scale; transperline = DIV_ROUND_UP(transperline, divisor); - for (i = 0, yield = yieldlines; i < linestotrans; i++) - { - for (pixels_read = j = 0; j < transperline; j++) - { + for (i = 0, yield = yieldlines; i < linestotrans; i++) { + for (pixels_read = j = 0; j < transperline; j++) { bytes = qc_readbytes(q, buffer); - for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) - { + for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) { int o; - if (buffer[k] == 0 && invert == 16) - { + if (buffer[k] == 0 && invert == 16) { /* 4bpp is odd (again) -- inverter is 16, not 15, but output must be 0-15 -- bls */ buffer[k] = 16; } - o=i*pixels_per_line + pixels_read + k; - if(o<len) - { + o = i * pixels_per_line + pixels_read + k; + if (o < len) { got++; - put_user((invert - buffer[k])<<shift, buf+o); + put_user((invert - buffer[k]) << shift, buf + o); } } pixels_read += bytes; } - (void) qc_readbytes(q, NULL); /* reset state machine */ + qc_readbytes(q, NULL); /* reset state machine */ /* Grabbing an entire frame from the quickcam is a lengthy process. We don't (usually) want to busy-block the @@ -690,14 +659,13 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l } } - if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) - { + if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) { write_lpcontrol(q, 2); write_lpcontrol(q, 6); udelay(3); write_lpcontrol(q, 0xe); } - if(got<len) + if (got < len) return got; return len; } @@ -709,11 +677,10 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); - struct qcam_device *qcam=(struct qcam_device *)dev; + struct qcam_device *qcam = (struct qcam_device *)dev; - switch(cmd) - { - case VIDIOCGCAP: + switch (cmd) { + case VIDIOCGCAP: { struct video_capability *b = arg; strcpy(b->name, "Quickcam"); @@ -726,73 +693,73 @@ static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) b->minheight = 60; return 0; } - case VIDIOCGCHAN: + case VIDIOCGCHAN: { struct video_channel *v = arg; - if(v->channel!=0) + if (v->channel != 0) return -EINVAL; - v->flags=0; - v->tuners=0; + v->flags = 0; + v->tuners = 0; /* Good question.. its composite or SVHS so.. */ v->type = VIDEO_TYPE_CAMERA; strcpy(v->name, "Camera"); return 0; } - case VIDIOCSCHAN: + case VIDIOCSCHAN: { struct video_channel *v = arg; - if(v->channel!=0) + if (v->channel != 0) return -EINVAL; return 0; } - case VIDIOCGTUNER: + case VIDIOCGTUNER: { struct video_tuner *v = arg; - if(v->tuner) + if (v->tuner) return -EINVAL; strcpy(v->name, "Format"); - v->rangelow=0; - v->rangehigh=0; - v->flags= 0; + v->rangelow = 0; + v->rangehigh = 0; + v->flags = 0; v->mode = VIDEO_MODE_AUTO; return 0; } - case VIDIOCSTUNER: + case VIDIOCSTUNER: { struct video_tuner *v = arg; - if(v->tuner) + if (v->tuner) return -EINVAL; - if(v->mode!=VIDEO_MODE_AUTO) + if (v->mode != VIDEO_MODE_AUTO) return -EINVAL; return 0; } - case VIDIOCGPICT: + case VIDIOCGPICT: { struct video_picture *p = arg; - p->colour=0x8000; - p->hue=0x8000; - p->brightness=qcam->brightness<<8; - p->contrast=qcam->contrast<<8; - p->whiteness=qcam->whitebal<<8; - p->depth=qcam->bpp; - p->palette=VIDEO_PALETTE_GREY; + p->colour = 0x8000; + p->hue = 0x8000; + p->brightness = qcam->brightness << 8; + p->contrast = qcam->contrast << 8; + p->whiteness = qcam->whitebal << 8; + p->depth = qcam->bpp; + p->palette = VIDEO_PALETTE_GREY; return 0; } - case VIDIOCSPICT: + case VIDIOCSPICT: { struct video_picture *p = arg; - if(p->palette!=VIDEO_PALETTE_GREY) + if (p->palette != VIDEO_PALETTE_GREY) return -EINVAL; - if(p->depth!=4 && p->depth!=6) + if (p->depth != 4 && p->depth != 6) return -EINVAL; /* * Now load the camera. */ - qcam->brightness = p->brightness>>8; - qcam->contrast = p->contrast>>8; - qcam->whitebal = p->whiteness>>8; + qcam->brightness = p->brightness >> 8; + qcam->contrast = p->contrast >> 8; + qcam->whitebal = p->whiteness >> 8; qcam->bpp = p->depth; mutex_lock(&qcam->lock); @@ -802,28 +769,25 @@ static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) return 0; } - case VIDIOCSWIN: + case VIDIOCSWIN: { struct video_window *vw = arg; - if(vw->flags) + if (vw->flags) return -EINVAL; - if(vw->clipcount) + if (vw->clipcount) return -EINVAL; - if(vw->height<60||vw->height>240) + if (vw->height < 60 || vw->height > 240) return -EINVAL; - if(vw->width<80||vw->width>320) + if (vw->width < 80 || vw->width > 320) return -EINVAL; qcam->width = 320; qcam->height = 240; qcam->transfer_scale = 4; - if(vw->width>=160 && vw->height>=120) - { + if (vw->width >= 160 && vw->height >= 120) qcam->transfer_scale = 2; - } - if(vw->width>=320 && vw->height>=240) - { + if (vw->width >= 320 && vw->height >= 240) { qcam->width = 320; qcam->height = 240; qcam->transfer_scale = 1; @@ -839,41 +803,42 @@ static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Ok we figured out what to use from our wide choice */ return 0; } - case VIDIOCGWIN: + case VIDIOCGWIN: { struct video_window *vw = arg; + memset(vw, 0, sizeof(*vw)); - vw->width=qcam->width/qcam->transfer_scale; - vw->height=qcam->height/qcam->transfer_scale; + vw->width = qcam->width / qcam->transfer_scale; + vw->height = qcam->height / qcam->transfer_scale; return 0; } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; } return 0; } static long qcam_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { return video_usercopy(file, cmd, arg, qcam_do_ioctl); } static ssize_t qcam_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { struct video_device *v = video_devdata(file); - struct qcam_device *qcam=(struct qcam_device *)v; + struct qcam_device *qcam = (struct qcam_device *)v; int len; parport_claim_or_block(qcam->pdev); @@ -885,7 +850,7 @@ static ssize_t qcam_read(struct file *file, char __user *buf, if (qcam->status & QC_PARAM_CHANGE) qc_set(qcam); - len=qc_capture(qcam, buf,count); + len = qc_capture(qcam, buf, count); mutex_unlock(&qcam->lock); @@ -917,8 +882,7 @@ static const struct v4l2_file_operations qcam_fops = { .ioctl = qcam_ioctl, .read = qcam_read, }; -static struct video_device qcam_template= -{ +static struct video_device qcam_template = { .name = "Connectix Quickcam", .fops = &qcam_fops, .release = video_device_release_empty, @@ -932,22 +896,20 @@ static int init_bwqcam(struct parport *port) { struct qcam_device *qcam; - if (num_cams == MAX_CAMS) - { + if (num_cams == MAX_CAMS) { printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS); return -ENOSPC; } - qcam=qcam_init(port); - if(qcam==NULL) + qcam = qcam_init(port); + if (qcam == NULL) return -ENODEV; parport_claim_or_block(qcam->pdev); qc_reset(qcam); - if(qc_detect(qcam)==0) - { + if (qc_detect(qcam) == 0) { parport_release(qcam->pdev); parport_unregister_device(qcam->pdev); kfree(qcam); @@ -1045,12 +1007,12 @@ static int __init init_bw_qcams(void) #ifdef MODULE /* Do some sanity checks on the module parameters. */ if (maxpoll > 5000) { - printk("Connectix Quickcam max-poll was above 5000. Using 5000.\n"); + printk(KERN_INFO "Connectix Quickcam max-poll was above 5000. Using 5000.\n"); maxpoll = 5000; } if (yieldlines < 1) { - printk("Connectix Quickcam yieldlines was less than 1. Using 1.\n"); + printk(KERN_INFO "Connectix Quickcam yieldlines was less than 1. Using 1.\n"); yieldlines = 1; } #endif diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index e2cbebab959b..8f1dd88b32a6 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -79,17 +79,17 @@ static inline void qcam_set_ack(struct qcam_device *qcam, unsigned int i) { /* note: the QC specs refer to the PCAck pin by voltage, not software level. PC ports have builtin inverters. */ - parport_frob_control(qcam->pport, 8, i?8:0); + parport_frob_control(qcam->pport, 8, i ? 8 : 0); } static inline unsigned int qcam_ready1(struct qcam_device *qcam) { - return (parport_read_status(qcam->pport) & 0x8)?1:0; + return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0; } static inline unsigned int qcam_ready2(struct qcam_device *qcam) { - return (parport_read_data(qcam->pport) & 0x1)?1:0; + return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0; } static unsigned int qcam_await_ready1(struct qcam_device *qcam, @@ -99,14 +99,13 @@ static unsigned int qcam_await_ready1(struct qcam_device *qcam, unsigned int i; for (oldjiffies = jiffies; - time_before(jiffies, oldjiffies + msecs_to_jiffies(40)); ) + time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) if (qcam_ready1(qcam) == value) return 0; /* If the camera didn't respond within 1/25 second, poll slowly for a while. */ - for (i = 0; i < 50; i++) - { + for (i = 0; i < 50; i++) { if (qcam_ready1(qcam) == value) return 0; msleep_interruptible(100); @@ -125,14 +124,13 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) unsigned int i; for (oldjiffies = jiffies; - time_before(jiffies, oldjiffies + msecs_to_jiffies(40)); ) + time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) if (qcam_ready2(qcam) == value) return 0; /* If the camera didn't respond within 1/25 second, poll slowly for a while. */ - for (i = 0; i < 50; i++) - { + for (i = 0; i < 50; i++) { if (qcam_ready2(qcam) == value) return 0; msleep_interruptible(100); @@ -149,22 +147,25 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) static int qcam_read_data(struct qcam_device *qcam) { unsigned int idata; + qcam_set_ack(qcam, 0); - if (qcam_await_ready1(qcam, 1)) return -1; + if (qcam_await_ready1(qcam, 1)) + return -1; idata = parport_read_status(qcam->pport) & 0xf0; qcam_set_ack(qcam, 1); - if (qcam_await_ready1(qcam, 0)) return -1; - idata |= (parport_read_status(qcam->pport) >> 4); + if (qcam_await_ready1(qcam, 0)) + return -1; + idata |= parport_read_status(qcam->pport) >> 4; return idata; } static int qcam_write_data(struct qcam_device *qcam, unsigned int data) { unsigned int idata; + parport_write_data(qcam->pport, data); idata = qcam_read_data(qcam); - if (data != idata) - { + if (data != idata) { printk(KERN_WARNING "cqcam: sent %x but received %x\n", data, idata); return 1; @@ -212,13 +213,12 @@ static int qc_detect(struct qcam_device *qcam) /* look for a heartbeat */ ostat = stat = parport_read_status(qcam->pport); - for (i=0; i<250; i++) - { + for (i = 0; i < 250; i++) { mdelay(1); stat = parport_read_status(qcam->pport); - if (ostat != stat) - { - if (++count >= 3) return 1; + if (ostat != stat) { + if (++count >= 3) + return 1; ostat = stat; } } @@ -232,13 +232,12 @@ static int qc_detect(struct qcam_device *qcam) count = 0; ostat = stat = parport_read_status(qcam->pport); - for (i=0; i<250; i++) - { + for (i = 0; i < 250; i++) { mdelay(1); stat = parport_read_status(qcam->pport); - if (ostat != stat) - { - if (++count >= 3) return 1; + if (ostat != stat) { + if (++count >= 3) + return 1; ostat = stat; } } @@ -263,7 +262,7 @@ static void qc_setup(struct qcam_device *q) { qc_reset(q); - /* Set the brightness. */ + /* Set the brightness. */ qcam_set(q, 11, q->brightness); /* Set the height and width. These refer to the actual @@ -292,25 +291,25 @@ static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, u unsigned int bytes = 0; qcam_set_ack(q, 0); - if (q->bidirectional) - { + if (q->bidirectional) { /* It's a bidirectional port */ - while (bytes < nbytes) - { + while (bytes < nbytes) { unsigned int lo1, hi1, lo2, hi2; unsigned char r, g, b; - if (qcam_await_ready2(q, 1)) return bytes; + if (qcam_await_ready2(q, 1)) + return bytes; lo1 = parport_read_data(q->pport) >> 1; hi1 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; qcam_set_ack(q, 1); - if (qcam_await_ready2(q, 0)) return bytes; + if (qcam_await_ready2(q, 0)) + return bytes; lo2 = parport_read_data(q->pport) >> 1; hi2 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; qcam_set_ack(q, 0); - r = (lo1 | ((hi1 & 1)<<7)); - g = ((hi1 & 0x1e)<<3) | ((hi2 & 0x1e)>>1); - b = (lo2 | ((hi2 & 1)<<7)); + r = lo1 | ((hi1 & 1) << 7); + g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1); + b = lo2 | ((hi2 & 1) << 7); if (force_rgb) { buf[bytes++] = r; buf[bytes++] = g; @@ -321,21 +320,20 @@ static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, u buf[bytes++] = r; } } - } - else - { + } else { /* It's a unidirectional port */ int i = 0, n = bytes; unsigned char rgb[3]; - while (bytes < nbytes) - { + while (bytes < nbytes) { unsigned int hi, lo; - if (qcam_await_ready1(q, 1)) return bytes; + if (qcam_await_ready1(q, 1)) + return bytes; hi = (parport_read_status(q->pport) & 0xf0); qcam_set_ack(q, 1); - if (qcam_await_ready1(q, 0)) return bytes; + if (qcam_await_ready1(q, 0)) + return bytes; lo = (parport_read_status(q->pport) & 0xf0); qcam_set_ack(q, 0); /* flip some bits */ @@ -374,28 +372,26 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le return -EFAULT; /* Wait for camera to become ready */ - for (;;) - { + for (;;) { int i = qcam_get(q, 41); + if (i == -1) { qc_setup(q); return -EIO; } if ((i & 0x80) == 0) break; - else - schedule(); + schedule(); } - if (qcam_set(q, 7, (q->mode | (is_bi_dir?1:0)) + 1)) + if (qcam_set(q, 7, (q->mode | (is_bi_dir ? 1 : 0)) + 1)) return -EIO; lines = q->height; pixelsperline = q->width; bitsperxfer = (is_bi_dir) ? 24 : 8; - if (is_bi_dir) - { + if (is_bi_dir) { /* Turn the port around */ parport_data_reverse(q->pport); mdelay(3); @@ -413,16 +409,17 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le wantlen = lines * pixelsperline * 24 / 8; - while (wantlen) - { + while (wantlen) { size_t t, s; - s = (wantlen > BUFSZ)?BUFSZ:wantlen; + + s = (wantlen > BUFSZ) ? BUFSZ : wantlen; t = qcam_read_bytes(q, tmpbuf, s); - if (outptr < len) - { + if (outptr < len) { size_t sz = len - outptr; - if (sz > t) sz = t; - if (__copy_to_user(buf+outptr, tmpbuf, sz)) + + if (sz > t) + sz = t; + if (__copy_to_user(buf + outptr, tmpbuf, sz)) break; outptr += sz; } @@ -434,33 +431,31 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le len = outptr; - if (wantlen) - { - printk("qcam: short read.\n"); + if (wantlen) { + printk(KERN_ERR "qcam: short read.\n"); if (is_bi_dir) parport_data_forward(q->pport); qc_setup(q); return len; } - if (is_bi_dir) - { + if (is_bi_dir) { int l; + do { l = qcam_read_bytes(q, tmpbuf, 3); cond_resched(); } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); if (force_rgb) { if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) - printk("qcam: bad EOF\n"); + printk(KERN_ERR "qcam: bad EOF\n"); } else { if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) - printk("qcam: bad EOF\n"); + printk(KERN_ERR "qcam: bad EOF\n"); } qcam_set_ack(q, 0); - if (qcam_await_ready1(q, 1)) - { - printk("qcam: no ack after EOF\n"); + if (qcam_await_ready1(q, 1)) { + printk(KERN_ERR "qcam: no ack after EOF\n"); parport_data_forward(q->pport); qc_setup(q); return len; @@ -468,27 +463,25 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le parport_data_forward(q->pport); mdelay(3); qcam_set_ack(q, 1); - if (qcam_await_ready1(q, 0)) - { - printk("qcam: no ack to port turnaround\n"); + if (qcam_await_ready1(q, 0)) { + printk(KERN_ERR "qcam: no ack to port turnaround\n"); qc_setup(q); return len; } - } - else - { + } else { int l; + do { l = qcam_read_bytes(q, tmpbuf, 1); cond_resched(); } while (l && tmpbuf[0] == 0x7e); - l = qcam_read_bytes(q, tmpbuf+1, 2); + l = qcam_read_bytes(q, tmpbuf + 1, 2); if (force_rgb) { if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) - printk("qcam: bad EOF\n"); + printk(KERN_ERR "qcam: bad EOF\n"); } else { if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) - printk("qcam: bad EOF\n"); + printk(KERN_ERR "qcam: bad EOF\n"); } } @@ -503,164 +496,166 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le static long qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); - struct qcam_device *qcam=(struct qcam_device *)dev; + struct qcam_device *qcam = (struct qcam_device *)dev; - switch(cmd) + switch (cmd) { + case VIDIOCGCAP: { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - strcpy(b->name, "Quickcam"); - b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; - b->channels = 1; - b->audios = 0; - b->maxwidth = 320; - b->maxheight = 240; - b->minwidth = 80; - b->minheight = 60; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - if(v->channel!=0) - return -EINVAL; - v->flags=0; - v->tuners=0; - /* Good question.. its composite or SVHS so.. */ - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Camera"); - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - if(v->channel!=0) - return -EINVAL; - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - memset(v,0,sizeof(*v)); - strcpy(v->name, "Format"); - v->mode = VIDEO_MODE_AUTO; - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - if(v->mode!=VIDEO_MODE_AUTO) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - p->colour=0x8000; - p->hue=0x8000; - p->brightness=qcam->brightness<<8; - p->contrast=qcam->contrast<<8; - p->whiteness=qcam->whitebal<<8; - p->depth=24; - p->palette=VIDEO_PALETTE_RGB24; - return 0; + struct video_capability *b = arg; + + strcpy(b->name, "Quickcam"); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + b->channels = 1; + b->audios = 0; + b->maxwidth = 320; + b->maxheight = 240; + b->minwidth = 80; + b->minheight = 60; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + /* Good question.. its composite or SVHS so.. */ + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Camera"); + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + + if (v->tuner) + return -EINVAL; + memset(v, 0, sizeof(*v)); + strcpy(v->name, "Format"); + v->mode = VIDEO_MODE_AUTO; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + + if (v->tuner) + return -EINVAL; + if (v->mode != VIDEO_MODE_AUTO) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + + p->colour = 0x8000; + p->hue = 0x8000; + p->brightness = qcam->brightness << 8; + p->contrast = qcam->contrast << 8; + p->whiteness = qcam->whitebal << 8; + p->depth = 24; + p->palette = VIDEO_PALETTE_RGB24; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + + /* + * Sanity check args + */ + if (p->depth != 24 || p->palette != VIDEO_PALETTE_RGB24) + return -EINVAL; + + /* + * Now load the camera. + */ + qcam->brightness = p->brightness >> 8; + qcam->contrast = p->contrast >> 8; + qcam->whitebal = p->whiteness >> 8; + + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (vw->height < 60 || vw->height > 240) + return -EINVAL; + if (vw->width < 80 || vw->width > 320) + return -EINVAL; + + qcam->width = 80; + qcam->height = 60; + qcam->mode = QC_DECIMATION_4; + + if (vw->width >= 160 && vw->height >= 120) { + qcam->width = 160; + qcam->height = 120; + qcam->mode = QC_DECIMATION_2; } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - - /* - * Sanity check args - */ - if (p->depth != 24 || p->palette != VIDEO_PALETTE_RGB24) - return -EINVAL; - - /* - * Now load the camera. - */ - qcam->brightness = p->brightness>>8; - qcam->contrast = p->contrast>>8; - qcam->whitebal = p->whiteness>>8; - - mutex_lock(&qcam->lock); - parport_claim_or_block(qcam->pdev); - qc_setup(qcam); - parport_release(qcam->pdev); - mutex_unlock(&qcam->lock); - return 0; + if (vw->width >= 320 && vw->height >= 240) { + qcam->width = 320; + qcam->height = 240; + qcam->mode = QC_DECIMATION_1; } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - - if(vw->flags) - return -EINVAL; - if(vw->clipcount) - return -EINVAL; - if(vw->height<60||vw->height>240) - return -EINVAL; - if(vw->width<80||vw->width>320) - return -EINVAL; - - qcam->width = 80; - qcam->height = 60; - qcam->mode = QC_DECIMATION_4; - - if(vw->width>=160 && vw->height>=120) - { - qcam->width = 160; - qcam->height = 120; - qcam->mode = QC_DECIMATION_2; - } - if(vw->width>=320 && vw->height>=240) - { - qcam->width = 320; - qcam->height = 240; - qcam->mode = QC_DECIMATION_1; - } - qcam->mode |= QC_MILLIONS; + qcam->mode |= QC_MILLIONS; #if 0 - if(vw->width>=640 && vw->height>=480) - { - qcam->width = 640; - qcam->height = 480; - qcam->mode = QC_BILLIONS | QC_DECIMATION_1; - } -#endif - /* Ok we figured out what to use from our - wide choice */ - mutex_lock(&qcam->lock); - parport_claim_or_block(qcam->pdev); - qc_setup(qcam); - parport_release(qcam->pdev); - mutex_unlock(&qcam->lock); - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - memset(vw, 0, sizeof(*vw)); - vw->width=qcam->width; - vw->height=qcam->height; - return 0; + if (vw->width >= 640 && vw->height >= 480) { + qcam->width = 640; + qcam->height = 480; + qcam->mode = QC_BILLIONS | QC_DECIMATION_1; } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; +#endif + /* Ok we figured out what to use from our + wide choice */ + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + memset(vw, 0, sizeof(*vw)); + vw->width = qcam->width; + vw->height = qcam->height; + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; } return 0; } @@ -675,13 +670,13 @@ static ssize_t qcam_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *v = video_devdata(file); - struct qcam_device *qcam=(struct qcam_device *)v; + struct qcam_device *qcam = (struct qcam_device *)v; int len; mutex_lock(&qcam->lock); parport_claim_or_block(qcam->pdev); /* Probably should have a semaphore against multiple users */ - len = qc_capture(qcam, buf,count); + len = qc_capture(qcam, buf, count); parport_release(qcam->pdev); mutex_unlock(&qcam->lock); return len; @@ -713,8 +708,7 @@ static const struct v4l2_file_operations qcam_fops = { .read = qcam_read, }; -static struct video_device qcam_template= -{ +static struct video_device qcam_template = { .name = "Colour QuickCam", .fops = &qcam_fops, .release = video_device_release_empty, @@ -727,17 +721,16 @@ static struct qcam_device *qcam_init(struct parport *port) struct qcam_device *q; q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); - if(q==NULL) + if (q == NULL) return NULL; q->pport = port; q->pdev = parport_register_device(port, "c-qcam", NULL, NULL, NULL, 0, NULL); - q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE)?1:0; + q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0; - if (q->pdev == NULL) - { + if (q->pdev == NULL) { printk(KERN_ERR "c-qcam: couldn't register for %s.\n", port->name); kfree(q); @@ -765,12 +758,11 @@ static int init_cqcam(struct parport *port) { struct qcam_device *qcam; - if (parport[0] != -1) - { + if (parport[0] != -1) { /* The user gave specific instructions */ int i, found = 0; - for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) - { + + for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) { if (parport[0] == port->number) found = 1; } @@ -782,15 +774,14 @@ static int init_cqcam(struct parport *port) return -ENOSPC; qcam = qcam_init(port); - if (qcam==NULL) + if (qcam == NULL) return -ENODEV; parport_claim_or_block(qcam->pdev); qc_reset(qcam); - if (probe && qc_detect(qcam)==0) - { + if (probe && qc_detect(qcam) == 0) { parport_release(qcam->pdev); parport_unregister_device(qcam->pdev); kfree(qcam); @@ -840,14 +831,14 @@ static struct parport_driver cqcam_driver = { .detach = cq_detach, }; -static int __init cqcam_init (void) +static int __init cqcam_init(void) { printk(BANNER "\n"); return parport_register_driver(&cqcam_driver); } -static void __exit cqcam_cleanup (void) +static void __exit cqcam_cleanup(void) { unsigned int i; @@ -862,9 +853,9 @@ MODULE_DESCRIPTION(BANNER); MODULE_LICENSE("GPL"); /* FIXME: parport=auto would never have worked, surely? --RR */ -MODULE_PARM_DESC(parport ,"parport=<auto|n[,n]...> for port detection method\n\ -probe=<0|1|2> for camera detection method\n\ -force_rgb=<0|1> for RGB data format (default BGR)"); +MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n" + "probe=<0|1|2> for camera detection method\n" + "force_rgb=<0|1> for RGB data format (default BGR)"); module_param_array(parport, int, NULL, 0); module_param(probe, int, 0); module_param(force_rgb, bool, 0); diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 6f91415eb7b4..5520789854da 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -324,7 +324,7 @@ static int cpia2_close(struct file *file) { if(fh->mmapped) cam->mmapped = 0; - v4l2_prio_close(&cam->prio,&fh->prio); + v4l2_prio_close(&cam->prio, fh->prio); file->private_data = NULL; kfree(fh); } @@ -1592,7 +1592,7 @@ static long cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_FMT: { struct cpia2_fh *fh = file->private_data; - retval = v4l2_prio_check(&cam->prio, &fh->prio); + retval = v4l2_prio_check(&cam->prio, fh->prio); if(retval) { mutex_unlock(&cam->busy_lock); return retval; diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 4392c76af5df..0e5006b14279 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -579,6 +579,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, u8 afe_mux_cfg; u8 adc2_cfg; + u8 input_mode; u32 afe_cfg; int i; @@ -589,6 +590,30 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, vid_input <= CX18_AV_COMPOSITE8) { afe_mux_cfg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); ch[0] = CVBS; + input_mode = 0x0; + } else if (vid_input >= CX18_AV_COMPONENT_LUMA1) { + int luma = vid_input & 0xf000; + int r_chroma = vid_input & 0xf0000; + int b_chroma = vid_input & 0xf00000; + + if ((vid_input & ~0xfff000) || + luma < CX18_AV_COMPONENT_LUMA1 || + luma > CX18_AV_COMPONENT_LUMA8 || + r_chroma < CX18_AV_COMPONENT_R_CHROMA4 || + r_chroma > CX18_AV_COMPONENT_R_CHROMA6 || + b_chroma < CX18_AV_COMPONENT_B_CHROMA7 || + b_chroma > CX18_AV_COMPONENT_B_CHROMA8) { + CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n", + vid_input); + return -EINVAL; + } + afe_mux_cfg = (luma - CX18_AV_COMPONENT_LUMA1) >> 12; + ch[0] = Y; + afe_mux_cfg |= (r_chroma - CX18_AV_COMPONENT_R_CHROMA4) >> 12; + ch[1] = Pr; + afe_mux_cfg |= (b_chroma - CX18_AV_COMPONENT_B_CHROMA7) >> 14; + ch[2] = Pb; + input_mode = 0x6; } else { int luma = vid_input & 0xf0; int chroma = vid_input & 0xf00; @@ -598,7 +623,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, luma > CX18_AV_SVIDEO_LUMA8 || chroma < CX18_AV_SVIDEO_CHROMA4 || chroma > CX18_AV_SVIDEO_CHROMA8) { - CX18_ERR_DEV(sd, "0x%04x is not a valid video input!\n", + CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n", vid_input); return -EINVAL; } @@ -613,8 +638,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; ch[1] = C; } + input_mode = 0x2; } - /* TODO: LeadTek WinFast DVR3100 H & WinFast PVR2100 can do Y/Pb/Pr */ switch (aud_input) { case CX18_AV_AUDIO_SERIAL1: @@ -650,8 +675,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, /* Set up analog front end multiplexers */ cx18_av_write_expect(cx, 0x103, afe_mux_cfg, afe_mux_cfg, 0xf7); - /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx18_av_and_or(cx, 0x401, ~0x6, ch[0] == CVBS ? 0 : 0x02); + /* Set INPUT_MODE to Composite, S-Video, or Component */ + cx18_av_and_or(cx, 0x401, ~0x6, input_mode); /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ adc2_cfg = cx18_av_read(cx, 0x102); @@ -998,9 +1023,9 @@ static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) static int cx18_av_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct cx18 *cx = v4l2_get_subdevdata(sd); - - return cx18_av_vbi_g_fmt(cx, fmt); + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + return cx18_av_g_sliced_fmt(sd, &fmt->fmt.sliced); } static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) @@ -1073,12 +1098,6 @@ static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) cx18_av_write(cx, 0x41e, 0x8 | filter); break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx18_av_vbi_s_fmt(cx, fmt); - - case V4L2_BUF_TYPE_VBI_CAPTURE: - return cx18_av_vbi_s_fmt(cx, fmt); - default: return -EINVAL; } @@ -1378,17 +1397,24 @@ static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { static const struct v4l2_subdev_video_ops cx18_av_video_ops = { .s_routing = cx18_av_s_video_routing, - .decode_vbi_line = cx18_av_decode_vbi_line, .s_stream = cx18_av_s_stream, .g_fmt = cx18_av_g_fmt, .s_fmt = cx18_av_s_fmt, }; +static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = { + .decode_vbi_line = cx18_av_decode_vbi_line, + .g_sliced_fmt = cx18_av_g_sliced_fmt, + .s_sliced_fmt = cx18_av_s_sliced_fmt, + .s_raw_fmt = cx18_av_s_raw_fmt, +}; + static const struct v4l2_subdev_ops cx18_av_ops = { .core = &cx18_av_general_ops, .tuner = &cx18_av_tuner_ops, .audio = &cx18_av_audio_ops, .video = &cx18_av_video_ops, + .vbi = &cx18_av_vbi_ops, }; int cx18_av_probe(struct cx18 *cx) diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index cafb7e99b9a0..c106967bdcc3 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -61,6 +61,25 @@ enum cx18_av_video_input { CX18_AV_SVIDEO2 = 0x620, CX18_AV_SVIDEO3 = 0x730, CX18_AV_SVIDEO4 = 0x840, + + /* Component Video inputs consist of one luma input (In1-In8) ORed + with a red chroma (In4-In6) and blue chroma input (In7-In8) */ + CX18_AV_COMPONENT_LUMA1 = 0x1000, + CX18_AV_COMPONENT_LUMA2 = 0x2000, + CX18_AV_COMPONENT_LUMA3 = 0x3000, + CX18_AV_COMPONENT_LUMA4 = 0x4000, + CX18_AV_COMPONENT_LUMA5 = 0x5000, + CX18_AV_COMPONENT_LUMA6 = 0x6000, + CX18_AV_COMPONENT_LUMA7 = 0x7000, + CX18_AV_COMPONENT_LUMA8 = 0x8000, + CX18_AV_COMPONENT_R_CHROMA4 = 0x40000, + CX18_AV_COMPONENT_R_CHROMA5 = 0x50000, + CX18_AV_COMPONENT_R_CHROMA6 = 0x60000, + CX18_AV_COMPONENT_B_CHROMA7 = 0x700000, + CX18_AV_COMPONENT_B_CHROMA8 = 0x800000, + + /* Component Video aliases for common combinations */ + CX18_AV_COMPONENT1 = 0x861000, }; enum cx18_av_audio_input { @@ -359,7 +378,8 @@ void cx18_av_audio_set_path(struct cx18 *cx); /* cx18_av-vbi.c */ int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); -int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt); -int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt); +int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); +int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); +int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); #endif diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index a51732bcca4b..baa36fbcd4d4 100644 --- a/drivers/media/video/cx18/cx18-av-vbi.c +++ b/drivers/media/video/cx18/cx18-av-vbi.c @@ -129,10 +129,10 @@ static int decode_vps(u8 *dst, u8 *p) return err & 0xf0; } -int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt) +int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) { + struct cx18 *cx = v4l2_get_subdevdata(sd); struct cx18_av_state *state = &cx->av_state; - struct v4l2_sliced_vbi_format *svbi; static const u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ @@ -143,9 +143,6 @@ int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt) int is_pal = !(state->std & V4L2_STD_525_60); int i; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; memset(svbi, 0, sizeof(*svbi)); /* we're done if raw VBI is active */ if ((cx18_av_read(cx, 0x404) & 0x10) == 0) @@ -173,30 +170,27 @@ int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt) return 0; } -int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt) +int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) { + struct cx18 *cx = v4l2_get_subdevdata(sd); struct cx18_av_state *state = &cx->av_state; - struct v4l2_sliced_vbi_format *svbi; - int is_pal = !(state->std & V4L2_STD_525_60); - int i, x; - u8 lcr[24]; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* raw VBI */ - memset(svbi, 0, sizeof(*svbi)); + /* Setup standard */ + cx18_av_std_setup(cx); - /* Setup standard */ - cx18_av_std_setup(cx); + /* VBI Offset */ + cx18_av_write(cx, 0x47f, state->slicer_line_delay); + cx18_av_write(cx, 0x404, 0x2e); + return 0; +} - /* VBI Offset */ - cx18_av_write(cx, 0x47f, state->slicer_line_delay); - cx18_av_write(cx, 0x404, 0x2e); - return 0; - } +int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + struct cx18_av_state *state = &cx->av_state; + int is_pal = !(state->std & V4L2_STD_525_60); + int i, x; + u8 lcr[24]; for (x = 0; x <= 23; x++) lcr[x] = 0x00; diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index f808fb6fc1c1..74e122b5fc49 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -370,6 +370,7 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = { { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, @@ -422,6 +423,7 @@ static const struct cx18_card cx18_card_leadtek_dvr3100h = { { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, @@ -480,7 +482,7 @@ int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input) "S-Video 2", "Composite 1", "Composite 2", - "Composite 3" + "Component 1" }; memset(input, 0, sizeof(*input)); diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index af3d71607dc9..796e517300ac 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -43,7 +43,7 @@ #define CX18_CARD_INPUT_SVIDEO2 3 #define CX18_CARD_INPUT_COMPOSITE1 4 #define CX18_CARD_INPUT_COMPOSITE2 5 -#define CX18_CARD_INPUT_COMPOSITE3 6 +#define CX18_CARD_INPUT_COMPONENT1 6 /* audio inputs */ #define CX18_CARD_INPUT_AUD_TUNER 1 @@ -62,7 +62,7 @@ struct cx18_card_video_input { u8 video_type; /* video input type */ u8 audio_index; /* index in cx18_card_audio_input array */ - u16 video_input; /* hardware video input */ + u32 video_input; /* hardware video input */ }; struct cx18_card_audio_input { diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 7fa589240ff2..4b4b46544d5a 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -263,7 +263,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) int ret; struct v4l2_control ctrl; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 863ce7758239..e12a15020cda 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -700,7 +700,7 @@ int cx18_v4l2_close(struct file *filp) CX18_DEBUG_IOCTL("close() of %s\n", s->name); - v4l2_prio_close(&cx->prio, &id->prio); + v4l2_prio_close(&cx->prio, id->prio); /* Easy case first: this stream was never claimed by us */ if (s->id != id->open_id) { diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index eecf29af916c..cfa1f289b0f5 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -109,7 +109,7 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case CX18_HW_Z8F0811_IR_RX_HAUP: - init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = IR_TYPE_RC5; init_data->name = cx->card_name; diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index b81dd0ea8eb9..2530fc54daaf 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -208,7 +208,7 @@ static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in * fmt->fmt.sliced under valid calling conditions */ - if (v4l2_subdev_call(cx->sd_av, video, g_fmt, fmt)) + if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced)) return -EINVAL; /* Ensure V4L2 spec compliant output */ @@ -277,7 +277,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, int ret; int w, h; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -306,7 +306,7 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -322,7 +322,7 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, * Note cx18_av_vbi_wipes out alot of the passed in fmt under valid * calling conditions */ - ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt); + ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi); if (ret) return ret; @@ -341,7 +341,7 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, int ret; struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -359,7 +359,7 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, * Note, cx18_av_vbi() wipes some "impossible" service lines in the * passed in fmt->fmt.sliced under valid calling conditions */ - ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt); + ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced); if (ret) return ret; /* Store our current v4l2 sliced VBI settings */ @@ -549,7 +549,7 @@ static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -601,7 +601,7 @@ int cx18_s_input(struct file *file, void *fh, unsigned int inp) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -647,7 +647,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -675,7 +675,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; @@ -715,7 +715,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) struct cx18 *cx = id->cx; int ret; - ret = v4l2_prio_check(&cx->prio, &id->prio); + ret = v4l2_prio_check(&cx->prio, id->prio); if (ret) return ret; diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 054450f65a60..f5c91261b2db 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -374,7 +374,10 @@ static void cx18_vbi_setup(struct cx18_stream *s) } /* setup VBI registers */ - v4l2_subdev_call(cx->sd_av, video, s_fmt, &cx->vbi.in); + if (raw) + v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi); + else + v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced); /* * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index 574c1c6974f8..582227522cf0 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -174,7 +174,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, p[3] != sliced_vbi_eav_rp[1])) continue; vbi.p = p + 4; - v4l2_subdev_call(cx->sd_av, video, decode_vbi_line, &vbi); + v4l2_subdev_call(cx->sd_av, vbi, decode_vbi_line, &vbi); if (vbi.type) { cx->vbi.sliced_data[line].id = vbi.type; cx->vbi.sliced_data[line].field = vbi.is_second_field; diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c index 7793d60966db..7cae95a2245e 100644 --- a/drivers/media/video/cx231xx/cx231xx-audio.c +++ b/drivers/media/video/cx231xx/cx231xx-audio.c @@ -495,7 +495,7 @@ static int cx231xx_audio_init(struct cx231xx *dev) pcm->info_flags = 0; pcm->private_data = dev; strcpy(pcm->name, "Conexant cx231xx Capture"); - strcpy(card->driver, "Conexant cx231xx Audio"); + strcpy(card->driver, "Cx231xx-Audio"); strcpy(card->shortname, "Cx231xx Audio"); strcpy(card->longname, "Conexant cx231xx Audio"); diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c index b24eee115e7e..f5e1a2315fcc 100644 --- a/drivers/media/video/cx231xx/cx231xx-core.c +++ b/drivers/media/video/cx231xx/cx231xx-core.c @@ -96,10 +96,9 @@ int cx231xx_register_extension(struct cx231xx_ops *ops) mutex_lock(&cx231xx_devlist_mutex); mutex_lock(&cx231xx_extension_devlist_lock); list_add_tail(&ops->next, &cx231xx_extension_devlist); - list_for_each_entry(dev, &cx231xx_devlist, devlist) { - if (dev) - ops->init(dev); - } + list_for_each_entry(dev, &cx231xx_devlist, devlist) + ops->init(dev); + printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name); mutex_unlock(&cx231xx_extension_devlist_lock); mutex_unlock(&cx231xx_devlist_mutex); @@ -112,10 +111,8 @@ void cx231xx_unregister_extension(struct cx231xx_ops *ops) struct cx231xx *dev = NULL; mutex_lock(&cx231xx_devlist_mutex); - list_for_each_entry(dev, &cx231xx_devlist, devlist) { - if (dev) - ops->fini(dev); - } + list_for_each_entry(dev, &cx231xx_devlist, devlist) + ops->fini(dev); mutex_lock(&cx231xx_extension_devlist_lock); printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name); diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c index b473cd8367f5..fd099153b746 100644 --- a/drivers/media/video/cx231xx/cx231xx-input.c +++ b/drivers/media/video/cx231xx/cx231xx-input.c @@ -35,6 +35,8 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); +#define MODULE_NAME "cx231xx" + #define i2cdprintk(fmt, arg...) \ if (ir_debug) { \ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ @@ -59,7 +61,6 @@ struct cx231xx_ir_poll_result { struct cx231xx_IR { struct cx231xx *dev; struct input_dev *input; - struct ir_input_state ir; char name[32]; char phys[32]; @@ -67,9 +68,7 @@ struct cx231xx_IR { int polling; struct work_struct work; struct timer_list timer; - unsigned int last_toggle:1; unsigned int last_readcount; - unsigned int repeat_interval; int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *); }; @@ -81,7 +80,6 @@ struct cx231xx_IR { static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) { int result; - int do_sendkey = 0; struct cx231xx_ir_poll_result poll_result; /* read the registers containing the IR status */ @@ -95,44 +93,23 @@ static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) poll_result.toggle_bit, poll_result.read_count, ir->last_readcount, poll_result.rc_data[0]); - if (ir->dev->chip_id == CHIP_ID_EM2874) { + if (poll_result.read_count > 0 && + poll_result.read_count != ir->last_readcount) + ir_keydown(ir->input, + poll_result.rc_data[0], + poll_result.toggle_bit); + + if (ir->dev->chip_id == CHIP_ID_EM2874) /* The em2874 clears the readcount field every time the register is read. The em2860/2880 datasheet says that it is supposed to clear the readcount, but it doesn't. So with the em2874, we are looking for a non-zero read count as opposed to a readcount that is incrementing */ ir->last_readcount = 0; - } - - if (poll_result.read_count == 0) { - /* The button has not been pressed since the last read */ - } else if (ir->last_toggle != poll_result.toggle_bit) { - /* A button has been pressed */ - dprintk("button has been pressed\n"); - ir->last_toggle = poll_result.toggle_bit; - ir->repeat_interval = 0; - do_sendkey = 1; - } else if (poll_result.toggle_bit == ir->last_toggle && - poll_result.read_count > 0 && - poll_result.read_count != ir->last_readcount) { - /* The button is still being held down */ - dprintk("button being held down\n"); - - /* Debouncer for first keypress */ - if (ir->repeat_interval++ > 9) { - /* Start repeating after 1 second */ - do_sendkey = 1; - } - } + else + ir->last_readcount = poll_result.read_count; - if (do_sendkey) { - dprintk("sending keypress\n"); - ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0]); - ir_input_nokey(ir->input, &ir->ir); } - - ir->last_readcount = poll_result.read_count; - return; } static void ir_timer(unsigned long data) @@ -198,10 +175,6 @@ int cx231xx_ir_init(struct cx231xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER); - if (err < 0) - goto err_out_free; - input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; @@ -217,7 +190,8 @@ int cx231xx_ir_init(struct cx231xx *dev) cx231xx_ir_start(ir); /* all done */ - err = ir_input_register(ir->input, dev->board.ir_codes, NULL); + err = __ir_input_register(ir->input, dev->board.ir_codes, + NULL, MODULE_NAME); if (err) goto err_out_stop; diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 16a73eab6726..2782709b263f 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -1669,7 +1669,7 @@ static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, f->fmt.sliced.service_set = 0; - call_all(dev, video, g_fmt, f); + call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); if (f->fmt.sliced.service_set == 0) rc = -EINVAL; @@ -1690,7 +1690,7 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return rc; mutex_lock(&dev->lock); - call_all(dev, video, g_fmt, f); + call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced); mutex_unlock(&dev->lock); if (f->fmt.sliced.service_set == 0) @@ -1902,9 +1902,12 @@ static int radio_queryctrl(struct file *file, void *priv, if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) return -EINVAL; if (c->id == V4L2_CID_AUDIO_MUTE) { - for (i = 0; i < CX231XX_CTLS; i++) + for (i = 0; i < CX231XX_CTLS; i++) { if (cx231xx_ctls[i].v.id == c->id) break; + } + if (i == CX231XX_CTLS) + return -EINVAL; *c = cx231xx_ctls[i].v; } else *c = no_ctl; diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index 17d4d1a800ce..38d417191a65 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -32,7 +32,7 @@ #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> -#include <media/ir-kbd-i2c.h> +#include <media/ir-core.h> #if defined(CONFIG_VIDEO_CX231XX_DVB) || \ defined(CONFIG_VIDEO_CX231XX_DVB_MODULE) #include <media/videobuf-dvb.h> diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 4c8e95853fa3..022b480918cd 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -1000,20 +1000,6 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, h, w); if (err) return err; } - - if (new->width != 720 || new->height != (new->is_50hz ? 576 : 480)) { - /* Adjust temporal filter if necessary. The problem with the - temporal filter is that it works well with full resolution - capturing, but not when the capture window is scaled (the - filter introduces a ghosting effect). So if the capture - window is scaled, then force the filter to 0. - - For full resolution the filter really improves the video - quality, especially if the original video quality is - suboptimal. */ - temporal = 0; - } - if (force || NEQ(stream_type)) { err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[new->stream_type]); diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c index d4a9d2c5947c..c95e7bc14745 100644 --- a/drivers/media/video/cx23885/cimax2.c +++ b/drivers/media/video/cx23885/cimax2.c @@ -60,12 +60,18 @@ static unsigned int ci_dbg; module_param(ci_dbg, int, 0644); MODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); +static unsigned int ci_irq_enable; +module_param(ci_irq_enable, int, 0644); +MODULE_PARM_DESC(ci_irq_enable, "Enable IRQ from CAM"); + #define ci_dbg_print(args...) \ do { \ if (ci_dbg) \ printk(KERN_DEBUG args); \ } while (0) +#define ci_irq_flags() (ci_irq_enable ? NETUP_IRQ_IRQAM : 0) + /* stores all private variables for communication with CI */ struct netup_ci_state { struct dvb_ca_en50221 ca; @@ -392,7 +398,7 @@ int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open if (0 != slot) return -EINVAL; - netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | NETUP_IRQ_IRQAM) + netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | ci_irq_flags()) : NETUP_IRQ_DETAM); return state->status; @@ -429,7 +435,7 @@ int netup_ci_init(struct cx23885_tsport *port) 0x01, /* power on (use it like store place) */ 0x00, /* RFU */ 0x00, /* int status read only */ - NETUP_IRQ_IRQAM | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */ + ci_irq_flags() | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */ 0x05, /* EXTINT=active-high, INT=push-pull */ 0x00, /* USCG1 */ 0x04, /* ack active low */ @@ -470,7 +476,7 @@ int netup_ci_init(struct cx23885_tsport *port) state->ca.poll_slot_status = netup_poll_ci_slot_status; state->ca.data = state; state->priv = port; - state->current_irq_mode = NETUP_IRQ_IRQAM | NETUP_IRQ_DETAM; + state->current_irq_mode = ci_irq_flags() | NETUP_IRQ_DETAM; ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 0, &cimax_init[0], 34); diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index a8ddc227cf86..abd64e89f60f 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -1356,7 +1356,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct cx23885_dev *dev = fh->dev; struct cx23885_tsport *tsport = &dev->ts1; - strcpy(cap->driver, dev->name); + strlcpy(cap->driver, dev->name, sizeof(cap->driver)); strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, sizeof(cap->card)); sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 939079d7bbb9..9e1460828b2f 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -1006,6 +1006,22 @@ static int dvb_register(struct cx23885_tsport *port) netup_ci_init(port); break; } + case CX23885_BOARD_TEVII_S470: { + u8 eeprom[256]; /* 24C02 i2c eeprom */ + + if (port->nr != 1) + break; + + /* Read entire EEPROM */ + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom)); + printk(KERN_INFO "TeVii S470 MAC= " + "%02X:%02X:%02X:%02X:%02X:%02X\n", + eeprom[0xa0], eeprom[0xa1], eeprom[0xa2], + eeprom[0xa3], eeprom[0xa4], eeprom[0xa5]); + memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6); + break; + } } return ret; diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c index 8e9d990dbe93..8d306d8bb61c 100644 --- a/drivers/media/video/cx23885/cx23885-input.c +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -51,6 +51,8 @@ #define RC5_EXTENDED_COMMAND_OFFSET 64 +#define MODULE_NAME "cx23885" + static inline unsigned int rc5_command(u32 rc5_baseband) { return RC5_INSTR(rc5_baseband) + @@ -338,7 +340,7 @@ int cx23885_input_init(struct cx23885_dev *dev) { struct card_ir *ir; struct input_dev *input_dev; - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; int ir_type, ir_addr, ir_start; int ret; @@ -353,7 +355,7 @@ int cx23885_input_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: /* Parameters for the grey Hauppauge remote for the HVR-1850 */ - ir_codes = &ir_codes_hauppauge_new_table; + ir_codes = RC_MAP_HAUPPAUGE_NEW; ir_type = IR_TYPE_RC5; ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */ ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */ @@ -398,7 +400,7 @@ int cx23885_input_init(struct cx23885_dev *dev) dev->ir_input = ir; cx23885_input_ir_start(dev); - ret = ir_input_register(ir->dev, ir_codes, NULL); + ret = ir_input_register(ir->dev, ir_codes, NULL, MODULE_NAME); if (ret) goto err_out_stop; diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 2d3ac8b83dc3..543b854f6a62 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -514,8 +514,8 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, *size = fh->fmt->depth*fh->width*fh->height >> 3; if (0 == *count) *count = 32; - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index f2461cd3de5a..8b6fb3544376 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -1018,7 +1018,7 @@ static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { switch (fmt->type) { case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx25840_vbi_g_fmt(sd, fmt); + return cx25840_g_sliced_fmt(sd, &fmt->fmt.sliced); default: return -EINVAL; } @@ -1079,12 +1079,6 @@ static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) cx25840_write(client, 0x41e, 0x8 | filter); break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx25840_vbi_s_fmt(sd, fmt); - - case V4L2_BUF_TYPE_VBI_CAPTURE: - return cx25840_vbi_s_fmt(sd, fmt); - default: return -EINVAL; } @@ -1635,15 +1629,22 @@ static const struct v4l2_subdev_video_ops cx25840_video_ops = { .s_routing = cx25840_s_video_routing, .g_fmt = cx25840_g_fmt, .s_fmt = cx25840_s_fmt, - .decode_vbi_line = cx25840_decode_vbi_line, .s_stream = cx25840_s_stream, }; +static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = { + .decode_vbi_line = cx25840_decode_vbi_line, + .s_raw_fmt = cx25840_s_raw_fmt, + .s_sliced_fmt = cx25840_s_sliced_fmt, + .g_sliced_fmt = cx25840_g_sliced_fmt, +}; + static const struct v4l2_subdev_ops cx25840_ops = { .core = &cx25840_core_ops, .tuner = &cx25840_tuner_ops, .audio = &cx25840_audio_ops, .video = &cx25840_video_ops, + .vbi = &cx25840_vbi_ops, }; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 55345444417f..04393b971567 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -99,8 +99,9 @@ int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); /* ----------------------------------------------------------------------- */ /* cx25850-vbi.c */ -int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt); -int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt); +int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); +int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); #endif diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c index 35f6592f6c47..64a4004f8a97 100644 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -82,11 +82,10 @@ static int decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct cx25840_state *state = to_state(sd); - struct v4l2_sliced_vbi_format *svbi; static const u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ @@ -97,9 +96,6 @@ int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) int is_pal = !(state->std & V4L2_STD_525_60); int i; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; memset(svbi, 0, sizeof(*svbi)); /* we're done if raw VBI is active */ if ((cx25840_read(client, 0x404) & 0x10) == 0) @@ -127,32 +123,30 @@ int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } -int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct cx25840_state *state = to_state(sd); - struct v4l2_sliced_vbi_format *svbi; int is_pal = !(state->std & V4L2_STD_525_60); int vbi_offset = is_pal ? 1 : 0; - int i, x; - u8 lcr[24]; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* raw VBI */ - memset(svbi, 0, sizeof(*svbi)); + /* Setup standard */ + cx25840_std_setup(client); - /* Setup standard */ - cx25840_std_setup(client); + /* VBI Offset */ + cx25840_write(client, 0x47f, vbi_offset); + cx25840_write(client, 0x404, 0x2e); + return 0; +} - /* VBI Offset */ - cx25840_write(client, 0x47f, vbi_offset); - cx25840_write(client, 0x404, 0x2e); - return 0; - } +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + int i, x; + u8 lcr[24]; for (x = 0; x <= 23; x++) lcr[x] = 0x00; diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index b35411160f04..8b21457111b1 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -847,7 +847,8 @@ static int set_tvaudio(struct cx88_core *core) { v4l2_std_id norm = core->tvnorm; - if (CX88_VMUX_TELEVISION != INPUT(core->input).type) + if (CX88_VMUX_TELEVISION != INPUT(core->input).type && + CX88_VMUX_CABLE != INPUT(core->input).type) return 0; if (V4L2_STD_PAL_BG & norm) { diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 94ab862f0219..faa8e8163a4a 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -1231,7 +1231,7 @@ static int dvb_register(struct cx8802_dev *dev) fe->ops.tuner_ops.set_config(fe, &ctl); } break; - case CX88_BOARD_PINNACLE_HYBRID_PCTV: + case CX88_BOARD_PINNACLE_HYBRID_PCTV: case CX88_BOARD_WINFAST_DTV1800H: fe0->dvb.frontend = dvb_attach(zl10353_attach, &cx88_pinnacle_hybrid_pctv, diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 6b6abf062c21..e185289e446c 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -32,12 +32,18 @@ #include "cx88.h" #include <media/ir-common.h> +#define MODULE_NAME "cx88xx" + /* ---------------------------------------------------------------------- */ struct cx88_IR { struct cx88_core *core; struct input_dev *input; struct ir_input_state ir; + struct ir_dev_props props; + + int users; + char name[32]; char phys[32]; @@ -159,8 +165,16 @@ static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) return HRTIMER_RESTART; } -void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) +static int __cx88_ir_start(void *priv) { + struct cx88_core *core = priv; + struct cx88_IR *ir; + + if (!core || !core->ir) + return -EINVAL; + + ir = core->ir; + if (ir->polling) { hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ir->timer.function = cx88_ir_work; @@ -173,10 +187,18 @@ void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) cx_write(MO_DDS_IO, 0xa80a80); /* 4 kHz sample rate */ cx_write(MO_DDSCFG_IO, 0x5); /* enable */ } + return 0; } -void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) +static void __cx88_ir_stop(void *priv) { + struct cx88_core *core = priv; + struct cx88_IR *ir; + + if (!core || !core->ir) + return; + + ir = core->ir; if (ir->sampling) { cx_write(MO_DDSCFG_IO, 0x0); core->pci_irqmask &= ~PCI_INT_IR_SMPINT; @@ -186,15 +208,49 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) hrtimer_cancel(&ir->timer); } +int cx88_ir_start(struct cx88_core *core) +{ + if (core->ir->users) + return __cx88_ir_start(core); + + return 0; +} + +void cx88_ir_stop(struct cx88_core *core) +{ + if (core->ir->users) + __cx88_ir_stop(core); +} + +static int cx88_ir_open(void *priv) +{ + struct cx88_core *core = priv; + + core->ir->users++; + return __cx88_ir_start(core); +} + +static void cx88_ir_close(void *priv) +{ + struct cx88_core *core = priv; + + core->ir->users--; + if (!core->ir->users) + __cx88_ir_stop(core); +} + /* ---------------------------------------------------------------------- */ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) { struct cx88_IR *ir; struct input_dev *input_dev; - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; + u32 hardware_mask = 0; /* For devices with a hardware mask, when + * used with a full-code IR table + */ ir = kzalloc(sizeof(*ir), GFP_KERNEL); input_dev = input_allocate_device(); @@ -208,15 +264,15 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_DNTV_LIVE_DVB_T: case CX88_BOARD_KWORLD_DVB_T: case CX88_BOARD_KWORLD_DVB_T_CX22702: - ir_codes = &ir_codes_dntv_live_dvb_t_table; + ir_codes = RC_MAP_DNTV_LIVE_DVB_T; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x60; ir->polling = 50; /* ms */ break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: - ir_codes = &ir_codes_cinergy_1400_table; - ir_type = IR_TYPE_PD; + ir_codes = RC_MAP_CINERGY_1400; + ir_type = IR_TYPE_NEC; ir->sampling = 0xeb04; /* address */ break; case CX88_BOARD_HAUPPAUGE: @@ -230,14 +286,14 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_PCHDTV_HD3000: case CX88_BOARD_PCHDTV_HD5500: case CX88_BOARD_HAUPPAUGE_IRONLY: - ir_codes = &ir_codes_hauppauge_new_table; + ir_codes = RC_MAP_HAUPPAUGE_NEW; ir_type = IR_TYPE_RC5; ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: case CX88_BOARD_WINFAST_DTV2000H_J: case CX88_BOARD_WINFAST_DTV1800H: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; ir->mask_keyup = 0x100; @@ -246,14 +302,14 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_WINFAST2000XP_EXPERT: case CX88_BOARD_WINFAST_DTV1000: case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; ir->mask_keyup = 0x100; ir->polling = 1; /* ms */ break; case CX88_BOARD_IODATA_GVBCTV7E: - ir_codes = &ir_codes_iodata_bctv7e_table; + ir_codes = RC_MAP_IODATA_BCTV7E; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0xfd; ir->mask_keydown = 0x02; @@ -261,36 +317,43 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_PROLINK_PLAYTVPVR: case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: - ir_codes = &ir_codes_pixelview_table; + /* + * It seems that this hardware is paired with NEC extended + * address 0x866b. So, unfortunately, its usage with other + * IR's with different address won't work. Still, there are + * other IR's from the same manufacturer that works, like the + * 002-T mini RC, provided with newer PV hardware + */ + ir_codes = RC_MAP_PIXELVIEW_MK12; ir->gpio_addr = MO_GP1_IO; - ir->mask_keycode = 0x1f; ir->mask_keyup = 0x80; - ir->polling = 1; /* ms */ + ir->polling = 10; /* ms */ + hardware_mask = 0x3f; /* Hardware returns only 6 bits from command part */ break; case CX88_BOARD_PROLINK_PV_8000GT: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: - ir_codes = &ir_codes_pixelview_new_table; + ir_codes = RC_MAP_PIXELVIEW_NEW; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x3f; ir->mask_keyup = 0x80; ir->polling = 1; /* ms */ break; case CX88_BOARD_KWORLD_LTV883: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x60; ir->polling = 1; /* ms */ break; case CX88_BOARD_ADSTECH_DVB_T_PCI: - ir_codes = &ir_codes_adstech_dvb_t_pci_table; + ir_codes = RC_MAP_ADSTECH_DVB_T_PCI; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0xbf; ir->mask_keyup = 0x40; ir->polling = 50; /* ms */ break; case CX88_BOARD_MSI_TVANYWHERE_MASTER: - ir_codes = &ir_codes_msi_tvanywhere_table; + ir_codes = RC_MAP_MSI_TVANYWHERE; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x1f; ir->mask_keyup = 0x40; @@ -298,7 +361,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_AVERTV_303: case CX88_BOARD_AVERTV_STUDIO_303: - ir_codes = &ir_codes_avertv_303_table; + ir_codes = RC_MAP_AVERTV_303; ir->gpio_addr = MO_GP2_IO; ir->mask_keycode = 0xfb; ir->mask_keydown = 0x02; @@ -311,41 +374,41 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_PROF_7300: case CX88_BOARD_PROF_7301: case CX88_BOARD_PROF_6200: - ir_codes = &ir_codes_tbs_nec_table; - ir_type = IR_TYPE_PD; + ir_codes = RC_MAP_TBS_NEC; + ir_type = IR_TYPE_NEC; ir->sampling = 0xff00; /* address */ break; case CX88_BOARD_TEVII_S460: case CX88_BOARD_TEVII_S420: - ir_codes = &ir_codes_tevii_nec_table; - ir_type = IR_TYPE_PD; + ir_codes = RC_MAP_TEVII_NEC; + ir_type = IR_TYPE_NEC; ir->sampling = 0xff00; /* address */ break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: - ir_codes = &ir_codes_dntv_live_dvbt_pro_table; - ir_type = IR_TYPE_PD; + ir_codes = RC_MAP_DNTV_LIVE_DVBT_PRO; + ir_type = IR_TYPE_NEC; ir->sampling = 0xff00; /* address */ break; case CX88_BOARD_NORWOOD_MICRO: - ir_codes = &ir_codes_norwood_table; + ir_codes = RC_MAP_NORWOOD; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x0e; ir->mask_keyup = 0x80; ir->polling = 50; /* ms */ break; case CX88_BOARD_NPGTECH_REALTV_TOP10FM: - ir_codes = &ir_codes_npgtech_table; + ir_codes = RC_MAP_NPGTECH; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0xfa; ir->polling = 50; /* ms */ break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: - ir_codes = &ir_codes_pinnacle_pctv_hd_table; + ir_codes = RC_MAP_PINNACLE_PCTV_HD; ir_type = IR_TYPE_RC5; ir->sampling = 1; break; case CX88_BOARD_POWERCOLOR_REAL_ANGEL: - ir_codes = &ir_codes_powercolor_real_angel_table; + ir_codes = RC_MAP_POWERCOLOR_REAL_ANGEL; ir->gpio_addr = MO_GP2_IO; ir->mask_keycode = 0x7e; ir->polling = 100; /* ms */ @@ -357,6 +420,21 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) goto err_out_free; } + /* + * The usage of mask_keycode were very convenient, due to several + * reasons. Among others, the scancode tables were using the scancode + * as the index elements. So, the less bits it was used, the smaller + * the table were stored. After the input changes, the better is to use + * the full scancodes, since it allows replacing the IR remote by + * another one. Unfortunately, there are still some hardware, like + * Pixelview Ultra Pro, where only part of the scancode is sent via + * GPIO. So, there's no way to get the full scancode. Due to that, + * hardware_mask were introduced here: it represents those hardware + * that has such limits. + */ + if (hardware_mask && !ir->mask_keycode) + ir->mask_keycode = hardware_mask; + /* init input device */ snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); @@ -381,19 +459,20 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->core = core; core->ir = ir; - cx88_ir_start(core, ir); + ir->props.priv = core; + ir->props.open = cx88_ir_open; + ir->props.close = cx88_ir_close; + ir->props.scanmask = hardware_mask; /* all done */ - err = ir_input_register(ir->input, ir_codes, NULL); + err = ir_input_register(ir->input, ir_codes, &ir->props, MODULE_NAME); if (err) - goto err_out_stop; + goto err_out_free; return 0; - err_out_stop: - cx88_ir_stop(core, ir); - core->ir = NULL; err_out_free: + core->ir = NULL; kfree(ir); return err; } @@ -406,7 +485,7 @@ int cx88_ir_fini(struct cx88_core *core) if (NULL == ir) return 0; - cx88_ir_stop(core, ir); + cx88_ir_stop(core); ir_input_unregister(ir->input); kfree(ir); diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 6aba7af9160a..499f8d512ad6 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -599,13 +599,22 @@ struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board static int cx8802_request_acquire(struct cx8802_driver *drv) { struct cx88_core *core = drv->core; + unsigned int i; /* Fail a request for hardware if the device is busy. */ if (core->active_type_id != CX88_BOARD_NONE && core->active_type_id != drv->type_id) return -EBUSY; - core->input = CX88_VMUX_DVB; + core->input = 0; + for (i = 0; + i < (sizeof(core->board.input) / sizeof(struct cx88_input)); + i++) { + if (core->board.input[i].type == CX88_VMUX_DVB) { + core->input = i; + break; + } + } if (drv->advise_acquire) { diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 48c450f4a85a..0fab65c3ab39 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -426,12 +426,13 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input) if (core->board.audio_chip && core->board.audio_chip == V4L2_IDENT_WM8775) { call_all(core, audio, s_routing, - INPUT(input).audioroute, 0, 0); + INPUT(input).audioroute, 0, 0); } /* cx2388's C-ADC is connected to the tuner only. When used with S-Video, that ADC is busy dealing with chroma, so an external must be used for baseband audio */ - if (INPUT(input).type != CX88_VMUX_TELEVISION ) { + if (INPUT(input).type != CX88_VMUX_TELEVISION && + INPUT(input).type != CX88_VMUX_CABLE) { /* "I2S ADC mode" */ core->tvaudio = WW_I2SADC; cx88_set_tvaudio(core); @@ -561,8 +562,8 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) *size = fh->fmt->depth*fh->width*fh->height >> 3; if (0 == *count) *count = 32; - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } @@ -1537,9 +1538,12 @@ static int radio_queryctrl (struct file *file, void *priv, c->id >= V4L2_CID_LASTP1) return -EINVAL; if (c->id == V4L2_CID_AUDIO_MUTE) { - for (i = 0; i < CX8800_CTLS; i++) + for (i = 0; i < CX8800_CTLS; i++) { if (cx8800_ctls[i].v.id == c->id) break; + } + if (i == CX8800_CTLS) + return -EINVAL; *c = cx8800_ctls[i].v; } else *c = no_ctl; @@ -1977,7 +1981,7 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev) } if (core->ir) - cx88_ir_stop(core, core->ir); + cx88_ir_stop(core); cx88_shutdown(core); /* FIXME */ pci_disable_device(pci_dev); @@ -2015,7 +2019,7 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) spin_unlock(&dev->slock); if (core->ir) - cx88_ir_stop(core, core->ir); + cx88_ir_stop(core); /* FIXME -- shutdown device */ cx88_shutdown(core); @@ -2056,7 +2060,7 @@ static int cx8800_resume(struct pci_dev *pci_dev) /* FIXME: re-initialize hardware */ cx88_reset(core); if (core->ir) - cx88_ir_start(core, core->ir); + cx88_ir_start(core); cx_set(MO_PCI_INTMSK, core->pci_irqmask); diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 48b6c04fb497..bdb03d336536 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -41,7 +41,7 @@ #include <linux/version.h> #include <linux/mutex.h> -#define CX88_VERSION_CODE KERNEL_VERSION(0,0,7) +#define CX88_VERSION_CODE KERNEL_VERSION(0, 0, 8) #define UNSET (-1U) @@ -290,7 +290,7 @@ struct cx88_subid { #define RESOURCE_VIDEO 2 #define RESOURCE_VBI 4 -#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* buffer for one video frame */ struct cx88_buffer { @@ -683,8 +683,8 @@ s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core); int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci); int cx88_ir_fini(struct cx88_core *core); void cx88_ir_irq(struct cx88_core *core); -void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir); -void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir); +int cx88_ir_start(struct cx88_core *core); +void cx88_ir_stop(struct cx88_core *core); /* ----------------------------------------------------------- */ /* cx88-mpeg.c */ diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c index b4cc96dc99ef..490aafb34e2f 100644 --- a/drivers/media/video/davinci/dm644x_ccdc.c +++ b/drivers/media/video/davinci/dm644x_ccdc.c @@ -101,6 +101,9 @@ static u32 ccdc_raw_bayer_pix_formats[] = static u32 ccdc_raw_yuv_pix_formats[] = {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; +/* CCDC Save/Restore context */ +static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)]; + /* register access routines */ static inline u32 regr(u32 offset) { @@ -400,7 +403,11 @@ void ccdc_config_ycbcr(void) * configure the FID, VD, HD pin polarity, * fld,hd pol positive, vd negative, 8-bit data */ - syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE | CCDC_SYN_MODE_8BITS; + syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE; + if (ccdc_cfg.if_type == VPFE_BT656_10BIT) + syn_mode |= CCDC_SYN_MODE_10BITS; + else + syn_mode |= CCDC_SYN_MODE_8BITS; } else { /* y/c external sync mode */ syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << @@ -419,8 +426,13 @@ void ccdc_config_ycbcr(void) * configure the order of y cb cr in SDRAM, and disable latch * internal register on vsync */ - regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | - CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + if (ccdc_cfg.if_type == VPFE_BT656_10BIT) + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT, + CCDC_CCDCFG); + else + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); /* * configure the horizontal line offset. This should be a @@ -435,7 +447,6 @@ void ccdc_config_ycbcr(void) ccdc_sbl_reset(); dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); - ccdc_readregs(); } static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) @@ -827,6 +838,7 @@ static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) case VPFE_BT656: case VPFE_YCBCR_SYNC_16: case VPFE_YCBCR_SYNC_8: + case VPFE_BT656_10BIT: ccdc_cfg.ycbcr.vd_pol = params->vdpol; ccdc_cfg.ycbcr.hd_pol = params->hdpol; break; @@ -837,6 +849,87 @@ static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) return 0; } +static void ccdc_save_context(void) +{ + ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR); + ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE); + ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID); + ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES); + ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO); + ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START); + ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES); + ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING); + ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF); + ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST); + ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR); + ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP); + ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB); + ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN); + ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP); + ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC); + ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR); + ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT); + ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW); + ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF); + ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG); + ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG); + ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ); + ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT); + ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0); + ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1); + ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2); + ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3); + ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4); + ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5); + ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6); + ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7); + ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0); + ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1); + ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0); + ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1); + ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT); +} + +static void ccdc_restore_context(void) +{ + regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE); + regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID); + regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES); + regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO); + regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START); + regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES); + regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING); + regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF); + regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST); + regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR); + regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP); + regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB); + regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN); + regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP); + regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC); + regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR); + regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT); + regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW); + regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF); + regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG); + regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG); + regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ); + regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT); + regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0); + regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1); + regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2); + regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3); + regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4); + regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5); + regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6); + regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7); + regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0); + regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1); + regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0); + regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1); + regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT); + regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR); +} static struct ccdc_hw_device ccdc_hw_dev = { .name = "DM6446 CCDC", .owner = THIS_MODULE, @@ -945,10 +1038,40 @@ static int dm644x_ccdc_remove(struct platform_device *pdev) return 0; } +static int dm644x_ccdc_suspend(struct device *dev) +{ + /* Save CCDC context */ + ccdc_save_context(); + /* Disable CCDC */ + ccdc_enable(0); + /* Disable both master and slave clock */ + clk_disable(ccdc_cfg.mclk); + clk_disable(ccdc_cfg.sclk); + + return 0; +} + +static int dm644x_ccdc_resume(struct device *dev) +{ + /* Enable both master and slave clock */ + clk_enable(ccdc_cfg.mclk); + clk_enable(ccdc_cfg.sclk); + /* Restore CCDC context */ + ccdc_restore_context(); + + return 0; +} + +static const struct dev_pm_ops dm644x_ccdc_pm_ops = { + .suspend = dm644x_ccdc_suspend, + .resume = dm644x_ccdc_resume, +}; + static struct platform_driver dm644x_ccdc_driver = { .driver = { .name = "dm644x_ccdc", .owner = THIS_MODULE, + .pm = &dm644x_ccdc_pm_ops, }, .remove = __devexit_p(dm644x_ccdc_remove), .probe = dm644x_ccdc_probe, diff --git a/drivers/media/video/davinci/dm644x_ccdc_regs.h b/drivers/media/video/davinci/dm644x_ccdc_regs.h index 6e5d05324466..90370e414e2c 100644 --- a/drivers/media/video/davinci/dm644x_ccdc_regs.h +++ b/drivers/media/video/davinci/dm644x_ccdc_regs.h @@ -59,7 +59,7 @@ #define CCDC_PRGODD_0 0x8c #define CCDC_PRGODD_1 0x90 #define CCDC_VP_OUT 0x94 - +#define CCDC_REG_END 0x98 /*************************************************************** * Define for various register bit mask and shifts for CCDC @@ -135,11 +135,19 @@ #define CCDC_SYN_MODE_INPMOD_SHIFT 12 #define CCDC_SYN_MODE_INPMOD_MASK 3 #define CCDC_SYN_MODE_8BITS (7 << 8) +#define CCDC_SYN_MODE_10BITS (6 << 8) +#define CCDC_SYN_MODE_11BITS (5 << 8) +#define CCDC_SYN_MODE_12BITS (4 << 8) +#define CCDC_SYN_MODE_13BITS (3 << 8) +#define CCDC_SYN_MODE_14BITS (2 << 8) +#define CCDC_SYN_MODE_15BITS (1 << 8) +#define CCDC_SYN_MODE_16BITS (0 << 8) #define CCDC_SYN_FLDMODE_MASK 1 #define CCDC_SYN_FLDMODE_SHIFT 7 #define CCDC_REC656IF_BT656_EN 3 #define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2) #define CCDC_CCDCFG_Y8POS_SHIFT 11 +#define CCDC_CCDCFG_BW656_10BIT (1 << 5) #define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 #define CCDC_NO_CULLING 0xffff00ff #endif diff --git a/drivers/media/video/davinci/isif_regs.h b/drivers/media/video/davinci/isif_regs.h index f7b8893a2957..aa69a463c122 100644 --- a/drivers/media/video/davinci/isif_regs.h +++ b/drivers/media/video/davinci/isif_regs.h @@ -158,7 +158,7 @@ /* gain - offset masks */ #define GAIN_INTEGER_SHIFT 9 -#define OFFSET_MASK 0xFFF +#define OFFSET_MASK 0xFFF #define GAIN_SDRAM_EN_SHIFT 12 #define GAIN_IPIPE_EN_SHIFT 13 #define GAIN_H3A_EN_SHIFT 14 diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 398dbe71cb82..1c2588247289 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -475,6 +475,11 @@ static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); if (!ret) vpfe_dev->initialized = 1; + + /* Clear all VPFE/CCDC interrupts */ + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(-1); + unlock: mutex_unlock(&ccdc_lock); return ret; @@ -534,6 +539,16 @@ static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) list_del(&vpfe_dev->next_frm->queue); vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; addr = videobuf_to_dma_contig(vpfe_dev->next_frm); + + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + addr += vpfe_dev->field_off; ccdc_dev->hw_ops.setfbaddr(addr); } @@ -554,7 +569,6 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) { struct vpfe_device *vpfe_dev = dev_id; enum v4l2_field field; - unsigned long addr; int fid; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); @@ -562,7 +576,7 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) /* if streaming not started, don't do anything */ if (!vpfe_dev->started) - return IRQ_HANDLED; + goto clear_intr; /* only for 6446 this will be applicable */ if (NULL != ccdc_dev->hw_ops.reset) @@ -574,7 +588,7 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) "frame format is progressive...\n"); if (vpfe_dev->cur_frm != vpfe_dev->next_frm) vpfe_process_buffer_complete(vpfe_dev); - return IRQ_HANDLED; + goto clear_intr; } /* interlaced or TB capture check which field we are in hardware */ @@ -599,12 +613,9 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) * the CCDC memory address */ if (field == V4L2_FIELD_SEQ_TB) { - addr = - videobuf_to_dma_contig(vpfe_dev->cur_frm); - addr += vpfe_dev->field_off; - ccdc_dev->hw_ops.setfbaddr(addr); + vpfe_schedule_bottom_field(vpfe_dev); } - return IRQ_HANDLED; + goto clear_intr; } /* * if one field is just being captured configure @@ -624,6 +635,10 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) */ vpfe_dev->field_id = fid; } +clear_intr: + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; } @@ -635,8 +650,11 @@ static irqreturn_t vdint1_isr(int irq, void *dev_id) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); /* if streaming not started, don't do anything */ - if (!vpfe_dev->started) + if (!vpfe_dev->started) { + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); return IRQ_HANDLED; + } spin_lock(&vpfe_dev->dma_queue_lock); if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && @@ -644,6 +662,10 @@ static irqreturn_t vdint1_isr(int irq, void *dev_id) vpfe_dev->cur_frm == vpfe_dev->next_frm) vpfe_schedule_next_buffer(vpfe_dev); spin_unlock(&vpfe_dev->dma_queue_lock); + + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; } @@ -714,7 +736,7 @@ static int vpfe_release(struct file *file) /* Decrement device usrs counter */ vpfe_dev->usrs--; /* Close the priority */ - v4l2_prio_close(&vpfe_dev->prio, &fh->prio); + v4l2_prio_close(&vpfe_dev->prio, fh->prio); /* If this is the last file handle */ if (!vpfe_dev->usrs) { vpfe_dev->initialized = 0; @@ -1218,7 +1240,10 @@ static int vpfe_videobuf_setup(struct videobuf_queue *vq, struct vpfe_device *vpfe_dev = fh->vpfe_dev; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); - *size = config_params.device_bufsize; + *size = vpfe_dev->fmt.fmt.pix.sizeimage; + if (vpfe_dev->memory == V4L2_MEMORY_MMAP && + vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize) + *size = config_params.device_bufsize; if (*count < config_params.min_numbuffers) *count = config_params.min_numbuffers; @@ -1233,6 +1258,8 @@ static int vpfe_videobuf_prepare(struct videobuf_queue *vq, { struct vpfe_fh *fh = vq->priv_data; struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long addr; + int ret; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); @@ -1242,8 +1269,18 @@ static int vpfe_videobuf_prepare(struct videobuf_queue *vq, vb->height = vpfe_dev->fmt.fmt.pix.height; vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; vb->field = field; + + ret = videobuf_iolock(vq, vb, NULL);; + if (ret < 0) + return ret; + + addr = videobuf_to_dma_contig(vb); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + vb->state = VIDEOBUF_PREPARED; } - vb->state = VIDEOBUF_PREPARED; return 0; } @@ -1311,13 +1348,6 @@ static int vpfe_reqbufs(struct file *file, void *priv, return -EINVAL; } - if (V4L2_MEMORY_USERPTR == req_buf->memory) { - /* we don't support user ptr IO */ - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs:" - " USERPTR IO not supported\n"); - return -EINVAL; - } - ret = mutex_lock_interruptible(&vpfe_dev->lock); if (ret) return ret; @@ -1831,7 +1861,6 @@ static __init int vpfe_probe(struct platform_device *pdev) goto probe_free_dev_mem; } - mutex_lock(&ccdc_lock); /* Allocate memory for ccdc configuration */ ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL); if (NULL == ccdc_cfg) { @@ -1840,6 +1869,8 @@ static __init int vpfe_probe(struct platform_device *pdev) goto probe_free_lock; } + mutex_lock(&ccdc_lock); + strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32); /* Get VINT0 irq resource */ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -2015,18 +2046,14 @@ static int __devexit vpfe_remove(struct platform_device *pdev) return 0; } -static int -vpfe_suspend(struct device *dev) +static int vpfe_suspend(struct device *dev) { - /* add suspend code here later */ - return -1; + return 0; } -static int -vpfe_resume(struct device *dev) +static int vpfe_resume(struct device *dev) { - /* add resume code here later */ - return -1; + return 0; } static const struct dev_pm_ops vpfe_dev_pm_ops = { diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 2e5a7fb2d0c9..a7f48b53d3fc 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -869,7 +869,7 @@ static int vpif_release(struct file *filep) mutex_unlock(&common->lock); /* Close the priority */ - v4l2_prio_close(&ch->prio, &fh->prio); + v4l2_prio_close(&ch->prio, fh->prio); if (fh->initialized) ch->initialized = 0; @@ -1444,7 +1444,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) } } - ret = v4l2_prio_check(&ch->prio, &fh->prio); + ret = v4l2_prio_check(&ch->prio, fh->prio); if (0 != ret) return ret; @@ -1554,7 +1554,7 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index) } } - ret = v4l2_prio_check(&ch->prio, &fh->prio); + ret = v4l2_prio_check(&ch->prio, fh->prio); if (0 != ret) return ret; @@ -1710,7 +1710,7 @@ static int vpif_s_fmt_vid_cap(struct file *file, void *priv, } } - ret = v4l2_prio_check(&ch->prio, &fh->prio); + ret = v4l2_prio_check(&ch->prio, fh->prio); if (0 != ret) return ret; diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 13c3a1b97760..da07607cbc55 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -384,7 +384,7 @@ static int vpif_get_std_info(struct channel_obj *ch) int index; std_info->stdid = vid_ch->stdid; - if (!std_info) + if (!std_info->stdid) return -1; for (index = 0; index < ARRAY_SIZE(ch_params); index++) { @@ -671,7 +671,7 @@ static int vpif_release(struct file *filep) ch->initialized = 0; /* Close the priority */ - v4l2_prio_close(&ch->prio, &fh->prio); + v4l2_prio_close(&ch->prio, fh->prio); filep->private_data = NULL; fh->initialized = 0; kfree(fh); @@ -753,7 +753,7 @@ static int vpif_s_fmt_vid_out(struct file *file, void *priv, } /* Check for the priority */ - ret = v4l2_prio_check(&ch->prio, &fh->prio); + ret = v4l2_prio_check(&ch->prio, fh->prio); if (0 != ret) return ret; fh->initialized = 1; diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index bd783387b37d..e182abf476c9 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -491,7 +491,7 @@ static int em28xx_audio_init(struct em28xx *dev) strcpy(pcm->name, "Empia 28xx Capture"); snd_card_set_dev(card, &dev->udev->dev); - strcpy(card->driver, "Empia Em28xx Audio"); + strcpy(card->driver, "Em28xx-Audio"); strcpy(card->shortname, "Em28xx Audio"); strcpy(card->longname, "Empia Em28xx Audio"); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index b0fb08337710..3a4fd8514511 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -602,7 +602,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Gadmei UTV330+", .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, - .ir_codes = &ir_codes_gadmei_rm008z_table, + .ir_codes = RC_MAP_GADMEI_RM008Z, .decoder = EM28XX_SAA711X, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .input = { { @@ -681,6 +681,20 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = { + .name = "EM2860/TVP5150 Reference Design", + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2861_BOARD_PLEXTOR_PX_TV100U] = { .name = "Plextor ConvertX PX-TV100U", .tuner_type = TUNER_TNF_5335MF, @@ -777,7 +791,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = RC_MAP_HAUPPAUGE_NEW, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -802,7 +816,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = RC_MAP_HAUPPAUGE_NEW, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -828,7 +842,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = RC_MAP_HAUPPAUGE_NEW, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -854,7 +868,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_rc5_hauppauge_new_table, + .ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -880,7 +894,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_pinnacle_pctv_hd_table, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -906,7 +920,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_ati_tv_wonder_hd_600_table, + .ir_codes = RC_MAP_ATI_TV_WONDER_HD_600, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -932,7 +946,7 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = default_digital, - .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .ir_codes = RC_MAP_TERRATEC_CINERGY_XS, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1282,7 +1296,7 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA711X, .has_dvb = 1, .dvb_gpio = em2882_kworld_315u_digital, - .ir_codes = &ir_codes_kworld_315u_table, + .ir_codes = RC_MAP_KWORLD_315U, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, /* Analog mode - still not ready */ @@ -1404,10 +1418,14 @@ struct em28xx_board em28xx_boards[] = { }, [EM2882_BOARD_KWORLD_VS_DVBT] = { .name = "Kworld VS-DVB-T 323UR", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = kworld_330u_digital, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ + .ir_codes = RC_MAP_KWORLD_315U, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1430,7 +1448,7 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .ir_codes = RC_MAP_TERRATEC_CINERGY_XS, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1523,7 +1541,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .decoder = EM28XX_TVP5150, .tuner_gpio = default_tuner_gpio, - .ir_codes = &ir_codes_kaiomy_table, + .ir_codes = RC_MAP_KAIOMY, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1623,7 +1641,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = evga_indtube_digital, - .ir_codes = &ir_codes_evga_indtube_table, + .ir_codes = RC_MAP_EVGA_INDTUBE, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1672,6 +1690,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2862), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2863), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2870), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2881), @@ -1792,6 +1812,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, + {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT}, {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, }; @@ -2138,6 +2159,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) break; case EM2883_BOARD_KWORLD_HYBRID_330U: case EM2882_BOARD_DIKOM_DK300: + case EM2882_BOARD_KWORLD_VS_DVBT: ctl->demod = XC3028_FE_CHINA; ctl->fname = XC2028_DEFAULT_FIRMWARE; break; @@ -2313,21 +2335,21 @@ void em28xx_register_i2c_ir(struct em28xx *dev) switch (dev->model) { case EM2800_BOARD_TERRATEC_CINERGY_200: case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = &ir_codes_em_terratec_table; + dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; dev->init_data.get_key = em28xx_get_key_terratec; dev->init_data.name = "i2c IR (EM28XX Terratec)"; break; case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; break; case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = &ir_codes_rc5_hauppauge_new_table; + dev->init_data.ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW; dev->init_data.get_key = em28xx_get_key_em_haup; dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = &ir_codes_winfast_usbii_deluxe_table;; + dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE;; dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; break; diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index a41cc5566778..d3813ed789d9 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -782,11 +782,15 @@ int em28xx_resolution_set(struct em28xx *dev) em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); - /* If we don't set the start position to 4 in VBI mode, we end up - with line 21 being YUYV encoded instead of being in 8-bit - greyscale */ + /* If we don't set the start position to 2 in VBI mode, we end up + with line 20/21 being YUYV encoded instead of being in 8-bit + greyscale. The core of the issue is that line 21 (and line 23 for + PAL WSS) are inside of active video region, and as a result they + get the pixelformatting associated with that area. So by cropping + it out, we end up with the same format as the rest of the VBI + region */ if (em28xx_vbi_supported(dev) == 1) - em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2); + em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2); else em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); @@ -1174,21 +1178,18 @@ void em28xx_add_into_devlist(struct em28xx *dev) */ static LIST_HEAD(em28xx_extension_devlist); -static DEFINE_MUTEX(em28xx_extension_devlist_lock); int em28xx_register_extension(struct em28xx_ops *ops) { struct em28xx *dev = NULL; mutex_lock(&em28xx_devlist_mutex); - mutex_lock(&em28xx_extension_devlist_lock); list_add_tail(&ops->next, &em28xx_extension_devlist); list_for_each_entry(dev, &em28xx_devlist, devlist) { if (dev) ops->init(dev); } printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); - mutex_unlock(&em28xx_extension_devlist_lock); mutex_unlock(&em28xx_devlist_mutex); return 0; } @@ -1204,10 +1205,8 @@ void em28xx_unregister_extension(struct em28xx_ops *ops) ops->fini(dev); } - mutex_lock(&em28xx_extension_devlist_lock); printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); list_del(&ops->next); - mutex_unlock(&em28xx_extension_devlist_lock); mutex_unlock(&em28xx_devlist_mutex); } EXPORT_SYMBOL(em28xx_unregister_extension); @@ -1216,26 +1215,26 @@ void em28xx_init_extension(struct em28xx *dev) { struct em28xx_ops *ops = NULL; - mutex_lock(&em28xx_extension_devlist_lock); + mutex_lock(&em28xx_devlist_mutex); if (!list_empty(&em28xx_extension_devlist)) { list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->init) ops->init(dev); } } - mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); } void em28xx_close_extension(struct em28xx *dev) { struct em28xx_ops *ops = NULL; - mutex_lock(&em28xx_extension_devlist_lock); + mutex_lock(&em28xx_devlist_mutex); if (!list_empty(&em28xx_extension_devlist)) { list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->fini) ops->fini(dev); } } - mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index bcd3c371009b..cf1d8c3655fc 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -467,6 +467,7 @@ static int dvb_init(struct em28xx *dev) } dev->dvb = dvb; + mutex_lock(&dev->lock); em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); /* init frontend */ switch (dev->model) { @@ -506,6 +507,7 @@ static int dvb_init(struct em28xx *dev) case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: case EM2881_BOARD_PINNACLE_HYBRID_PRO: case EM2882_BOARD_DIKOM_DK300: + case EM2882_BOARD_KWORLD_VS_DVBT: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); @@ -589,15 +591,16 @@ static int dvb_init(struct em28xx *dev) if (result < 0) goto out_free; - em28xx_set_mode(dev, EM28XX_SUSPEND); em28xx_info("Successfully loaded em28xx-dvb\n"); - return 0; +ret: + em28xx_set_mode(dev, EM28XX_SUSPEND); + mutex_unlock(&dev->lock); + return result; out_free: - em28xx_set_mode(dev, EM28XX_SUSPEND); kfree(dvb); dev->dvb = NULL; - return result; + goto ret; } static int dvb_fini(struct em28xx *dev) diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 20a0001e8885..5c3fd9411b1f 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -39,6 +39,8 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); +#define MODULE_NAME "em28xx" + #define i2cdprintk(fmt, arg...) \ if (ir_debug) { \ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ @@ -360,14 +362,20 @@ static void em28xx_ir_work(struct work_struct *work) schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } -static void em28xx_ir_start(struct em28xx_IR *ir) +static int em28xx_ir_start(void *priv) { + struct em28xx_IR *ir = priv; + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); schedule_delayed_work(&ir->work, 0); + + return 0; } -static void em28xx_ir_stop(struct em28xx_IR *ir) +static void em28xx_ir_stop(void *priv) { + struct em28xx_IR *ir = priv; + cancel_delayed_work_sync(&ir->work); } @@ -380,7 +388,6 @@ int em28xx_ir_change_protocol(void *priv, u64 ir_type) /* Adjust xclk based o IR table for RC5/NEC tables */ - dev->board.ir_codes->ir_type = IR_TYPE_OTHER; if (ir_type == IR_TYPE_RC5) { dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; ir->full_code = 1; @@ -388,11 +395,9 @@ int em28xx_ir_change_protocol(void *priv, u64 ir_type) dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; ir_config = EM2874_IR_NEC; ir->full_code = 1; - } else + } else if (ir_type != IR_TYPE_UNKNOWN) rc = -EINVAL; - dev->board.ir_codes->ir_type = ir_type; - em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); @@ -443,6 +448,13 @@ int em28xx_ir_init(struct em28xx *dev) ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; ir->props.priv = ir; ir->props.change_protocol = em28xx_ir_change_protocol; + ir->props.open = em28xx_ir_start; + ir->props.close = em28xx_ir_stop; + + /* By default, keep protocol field untouched */ + err = em28xx_ir_change_protocol(ir, IR_TYPE_UNKNOWN); + if (err) + goto err_out_free; /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ @@ -455,7 +467,6 @@ int em28xx_ir_init(struct em28xx *dev) strlcat(ir->phys, "/input0", sizeof(ir->phys)); /* Set IR protocol */ - em28xx_ir_change_protocol(ir, dev->board.ir_codes->ir_type); err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER); if (err < 0) goto err_out_free; @@ -470,17 +481,15 @@ int em28xx_ir_init(struct em28xx *dev) input_dev->dev.parent = &dev->udev->dev; - em28xx_ir_start(ir); /* all done */ err = ir_input_register(ir->input, dev->board.ir_codes, - &ir->props); + &ir->props, MODULE_NAME); if (err) goto err_out_stop; return 0; err_out_stop: - em28xx_ir_stop(ir); dev->ir = NULL; err_out_free: kfree(ir); diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 0fe20110bfd6..20090e34173a 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -203,12 +203,6 @@ static void em28xx_copy_video(struct em28xx *dev, if (dma_q->pos + len > buf->vb.size) len = buf->vb.size - dma_q->pos; - if (p[0] != 0x88 && p[0] != 0x22) { - em28xx_isocdbg("frame is not complete\n"); - len += 4; - } else - p += 4; - startread = p; remain = len; @@ -309,14 +303,6 @@ static void em28xx_copy_vbi(struct em28xx *dev, if (dma_q->pos + len > buf->vb.size) len = buf->vb.size - dma_q->pos; - if ((p[0] == 0x33 && p[1] == 0x95) || - (p[0] == 0x88 && p[1] == 0x88)) { - /* Header field, advance past it */ - p += 4; - } else { - len += 4; - } - startread = p; startwrite = outp + dma_q->pos; @@ -507,8 +493,15 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) dma_q->pos = 0; } - if (buf != NULL) + if (buf != NULL) { + if (p[0] != 0x88 && p[0] != 0x22) { + em28xx_isocdbg("frame is not complete\n"); + len += 4; + } else { + p += 4; + } em28xx_copy_video(dev, dma_q, buf, p, outp, len); + } } return rc; } @@ -555,8 +548,7 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) continue; } - len = urb->iso_frame_desc[i].actual_length - 4; - + len = urb->iso_frame_desc[i].actual_length; if (urb->iso_frame_desc[i].actual_length <= 0) { /* em28xx_isocdbg("packet %d is empty",i); - spammy */ continue; @@ -577,6 +569,17 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) dev->vbi_read = 0; em28xx_isocdbg("VBI START HEADER!!!\n"); dev->cur_field = p[2]; + p += 4; + len -= 4; + } else if (p[0] == 0x88 && p[1] == 0x88 && + p[2] == 0x88 && p[3] == 0x88) { + /* continuation */ + p += 4; + len -= 4; + } else if (p[0] == 0x22 && p[1] == 0x5a) { + /* start video */ + p += 4; + len -= 4; } vbi_size = dev->vbi_width * dev->vbi_height; @@ -631,9 +634,6 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if (dev->capture_type == 1) { dev->capture_type = 2; - em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1) ? "odd" : "even"); - if (dev->progressive || !(dev->cur_field & 1)) { if (buf != NULL) buffer_filled(dev, dma_q, buf); @@ -652,8 +652,25 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) dma_q->pos = 0; } - if (buf != NULL && dev->capture_type == 2) - em28xx_copy_video(dev, dma_q, buf, p, outp, len); + + if (buf != NULL && dev->capture_type == 2) { + if (len > 4 && p[0] == 0x88 && p[1] == 0x88 && + p[2] == 0x88 && p[3] == 0x88) { + p += 4; + len -= 4; + } + if (len > 4 && p[0] == 0x22 && p[1] == 0x5a) { + em28xx_isocdbg("Video frame %d, len=%i, %s\n", + p[2], len, (p[2] & 1) ? + "odd" : "even"); + p += 4; + len -= 4; + } + + if (len > 0) + em28xx_copy_video(dev, dma_q, buf, p, outp, + len); + } } return rc; } @@ -1483,6 +1500,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Tuner"); + t->type = V4L2_TUNER_ANALOG_TV; mutex_lock(&dev->lock); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); @@ -1814,7 +1832,7 @@ static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, mutex_lock(&dev->lock); f->fmt.sliced.service_set = 0; - v4l2_device_call_all(&dev->v4l2_dev, 0, video, g_fmt, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); if (f->fmt.sliced.service_set == 0) rc = -EINVAL; @@ -1836,7 +1854,7 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return rc; mutex_lock(&dev->lock); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, g_fmt, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); mutex_unlock(&dev->lock); if (f->fmt.sliced.service_set == 0) diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index ba6fe5daff84..b252d1b1b2a7 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -68,6 +68,7 @@ #define EM2820_BOARD_HERCULES_SMART_TV_USB2 26 #define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27 #define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28 +#define EM2860_BOARD_TVP5150_REFERENCE_DESIGN 29 #define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 #define EM2821_BOARD_USBGEAR_VD204 31 #define EM2821_BOARD_SUPERCOMP_USB_2 32 @@ -140,10 +141,10 @@ #define EM28XX_NUM_BUFS 5 /* number of packets for each buffer - windows requests only 40 packets .. so we better do the same + windows requests only 64 packets .. so we better do the same this is what I found out for all alternate numbers there! */ -#define EM28XX_NUM_PACKETS 40 +#define EM28XX_NUM_PACKETS 64 #define EM28XX_INTERLACED_DEFAULT 1 @@ -411,7 +412,7 @@ struct em28xx_board { struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; - struct ir_scancode_table *ir_codes; + char *ir_codes; }; struct em28xx_eeprom { diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index e6c23d509862..a5cfc76b40b7 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1713,7 +1713,7 @@ et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { if (ctrl.id == s->qctrl[i].id) { if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) return -EINVAL; @@ -1723,7 +1723,9 @@ et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) ctrl.value -= ctrl.value % s->qctrl[i].step; break; } - + } + if (i == ARRAY_SIZE(s->qctrl)) + return -EINVAL; if ((err = s->set_ctrl(cam, &ctrl))) return err; diff --git a/drivers/media/video/font.h b/drivers/media/video/font.h deleted file mode 100644 index 8b1fecc37599..000000000000 --- a/drivers/media/video/font.h +++ /dev/null @@ -1,407 +0,0 @@ -static unsigned char rom8x16_bits[] = { -/* Character 0 (0x30): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** ** | - |** *** | - |** **** | - |**** ** | - |*** ** | - |** ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc6, -0xce, -0xde, -0xf6, -0xe6, -0xc6, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 1 (0x31): - ht=16, width=8 - +--------+ - | | - | | - | ** | - | **** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ****** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x18, -0x78, -0x18, -0x18, -0x18, -0x18, -0x18, -0x18, -0x18, -0x7e, -0x00, -0x00, -0x00, -0x00, - -/* Character 2 (0x32): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** ** | - | ** | - | ** | - | ** | - | ** | - | ** | - |** ** | - |******* | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc6, -0x06, -0x0c, -0x18, -0x30, -0x60, -0xc6, -0xfe, -0x00, -0x00, -0x00, -0x00, - -/* Character 3 (0x33): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - | ** | - | ** | - | **** | - | ** | - | ** | - | ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0x06, -0x06, -0x3c, -0x06, -0x06, -0x06, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 4 (0x34): - ht=16, width=8 - +--------+ - | | - | | - | ** | - | *** | - | **** | - | ** ** | - |** ** | - |** ** | - |******* | - | ** | - | ** | - | **** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x0c, -0x1c, -0x3c, -0x6c, -0xcc, -0xcc, -0xfe, -0x0c, -0x0c, -0x1e, -0x00, -0x00, -0x00, -0x00, - -/* Character 5 (0x35): - ht=16, width=8 - +--------+ - | | - | | - |******* | - |** | - |** | - |** | - |****** | - | ** | - | ** | - | ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0xfe, -0xc0, -0xc0, -0xc0, -0xfc, -0x06, -0x06, -0x06, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 6 (0x36): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** | - |** | - |****** | - |** ** | - |** ** | - |** ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc0, -0xc0, -0xfc, -0xc6, -0xc6, -0xc6, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 7 (0x37): - ht=16, width=8 - +--------+ - | | - | | - |******* | - |** ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | ** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0xfe, -0xc6, -0x06, -0x0c, -0x18, -0x30, -0x30, -0x30, -0x30, -0x30, -0x00, -0x00, -0x00, -0x00, - -/* Character 8 (0x38): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** ** | - |** ** | - | ***** | - |** ** | - |** ** | - |** ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc6, -0xc6, -0x7c, -0xc6, -0xc6, -0xc6, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, - -/* Character 9 (0x39): - ht=16, width=8 - +--------+ - | | - | | - | ***** | - |** ** | - |** ** | - |** ** | - |** ** | - | ****** | - | ** | - | ** | - |** ** | - | ***** | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x7c, -0xc6, -0xc6, -0xc6, -0xc6, -0x7e, -0x06, -0x06, -0xc6, -0x7c, -0x00, -0x00, -0x00, -0x00, -/* Character : (0x3a): - ht=16, width=8 - +--------+ - | | - | | - | | - | | - | | - | ** | - | ** | - | | - | | - | ** | - | ** | - | | - | | - | | - | | - | | - +--------+ */ -0x00, -0x00, -0x00, -0x00, -0x00, -0x0c, -0x0c, -0x00, -0x00, -0x0c, -0x0c, -0x00, -0x00, -0x00, -0x00, -0x00, -}; diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index e0060c1f0544..5d920e584de7 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -172,12 +172,6 @@ config USB_GSPCA_SN9C20X To compile this driver as a module, choose M here: the module will be called gspca_sn9c20x. -config USB_GSPCA_SN9C20X_EVDEV - bool "Enable evdev support" - depends on USB_GSPCA_SN9C20X && INPUT - ---help--- - Say Y here in order to enable evdev support for sn9c20x webcam button. - config USB_GSPCA_SONIXB tristate "SONIX Bayer USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index 82945ed5cbe5..58b696f455be 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -374,7 +374,7 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -861,7 +861,7 @@ static int save_camera_state(struct gspca_dev *gspca_dev) return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); } -int command_setformat(struct gspca_dev *gspca_dev) +static int command_setformat(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret; @@ -878,7 +878,7 @@ int command_setformat(struct gspca_dev *gspca_dev) sd->params.roi.rowStart, sd->params.roi.rowEnd); } -int command_setcolourparams(struct gspca_dev *gspca_dev) +static int command_setcolourparams(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; return do_command(gspca_dev, CPIA_COMMAND_SetColourParams, @@ -887,7 +887,7 @@ int command_setcolourparams(struct gspca_dev *gspca_dev) sd->params.colourParams.saturation, 0); } -int command_setapcor(struct gspca_dev *gspca_dev) +static int command_setapcor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; return do_command(gspca_dev, CPIA_COMMAND_SetApcor, @@ -897,7 +897,7 @@ int command_setapcor(struct gspca_dev *gspca_dev) sd->params.apcor.gain8); } -int command_setvloffset(struct gspca_dev *gspca_dev) +static int command_setvloffset(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset, @@ -907,7 +907,7 @@ int command_setvloffset(struct gspca_dev *gspca_dev) sd->params.vlOffset.gain8); } -int command_setexposure(struct gspca_dev *gspca_dev) +static int command_setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret; @@ -943,7 +943,7 @@ int command_setexposure(struct gspca_dev *gspca_dev) return ret; } -int command_setcolourbalance(struct gspca_dev *gspca_dev) +static int command_setcolourbalance(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -973,7 +973,7 @@ int command_setcolourbalance(struct gspca_dev *gspca_dev) return -EINVAL; } -int command_setcompressiontarget(struct gspca_dev *gspca_dev) +static int command_setcompressiontarget(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -983,7 +983,7 @@ int command_setcompressiontarget(struct gspca_dev *gspca_dev) sd->params.compressionTarget.targetQ, 0); } -int command_setyuvtresh(struct gspca_dev *gspca_dev) +static int command_setyuvtresh(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -992,7 +992,7 @@ int command_setyuvtresh(struct gspca_dev *gspca_dev) sd->params.yuvThreshold.uvThreshold, 0, 0); } -int command_setcompressionparams(struct gspca_dev *gspca_dev) +static int command_setcompressionparams(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1009,7 +1009,7 @@ int command_setcompressionparams(struct gspca_dev *gspca_dev) sd->params.compressionParams.decimationThreshMod); } -int command_setcompression(struct gspca_dev *gspca_dev) +static int command_setcompression(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1018,7 +1018,7 @@ int command_setcompression(struct gspca_dev *gspca_dev) sd->params.compression.decimation, 0, 0); } -int command_setsensorfps(struct gspca_dev *gspca_dev) +static int command_setsensorfps(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1027,7 +1027,7 @@ int command_setsensorfps(struct gspca_dev *gspca_dev) sd->params.sensorFps.baserate, 0, 0); } -int command_setflickerctrl(struct gspca_dev *gspca_dev) +static int command_setflickerctrl(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1038,7 +1038,7 @@ int command_setflickerctrl(struct gspca_dev *gspca_dev) 0); } -int command_setecptiming(struct gspca_dev *gspca_dev) +static int command_setecptiming(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1046,12 +1046,12 @@ int command_setecptiming(struct gspca_dev *gspca_dev) sd->params.ecpTiming, 0, 0, 0); } -int command_pause(struct gspca_dev *gspca_dev) +static int command_pause(struct gspca_dev *gspca_dev) { return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); } -int command_resume(struct gspca_dev *gspca_dev) +static int command_resume(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1059,7 +1059,8 @@ int command_resume(struct gspca_dev *gspca_dev) 0, sd->params.streamStartLine, 0, 0); } -int command_setlights(struct gspca_dev *gspca_dev) +#if 0 /* Currently unused */ +static int command_setlights(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret, p1, p2; @@ -1078,6 +1079,7 @@ int command_setlights(struct gspca_dev *gspca_dev) return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0, p1 | p2 | 0xE0, 0); } +#endif static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply) { diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 222af479150b..efe615938783 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1,7 +1,7 @@ /* * Main USB camera driver * - * Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr) + * Copyright (C) 2008-2010 Jean-François Moine <http://moinejf.free.fr> * * Camera button input handling by Márton Németh * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu> @@ -35,12 +35,12 @@ #include <linux/io.h> #include <asm/page.h> #include <linux/uaccess.h> -#include <linux/jiffies.h> +#include <linux/ktime.h> #include <media/v4l2-ioctl.h> #include "gspca.h" -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) #include <linux/input.h> #include <linux/usb/input.h> #endif @@ -51,7 +51,7 @@ #error "DEF_NURBS too big" #endif -MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); +MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -115,7 +115,7 @@ static const struct vm_operations_struct gspca_vm_ops = { /* * Input and interrupt endpoint handling functions */ -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static void int_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; @@ -199,7 +199,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, void *buffer = NULL; int ret = -EINVAL; - buffer_len = ep->wMaxPacketSize; + buffer_len = le16_to_cpu(ep->wMaxPacketSize); interval = ep->bInterval; PDEBUG(D_PROBE, "found int in endpoint: 0x%x, " "buffer_len=%u, interval=%u", @@ -213,7 +213,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, goto error; } - buffer = usb_buffer_alloc(dev, ep->wMaxPacketSize, + buffer = usb_buffer_alloc(dev, buffer_len, GFP_KERNEL, &urb->transfer_dma); if (!buffer) { ret = -ENOMEM; @@ -280,9 +280,18 @@ static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) } } #else -#define gspca_input_connect(gspca_dev) 0 -#define gspca_input_create_urb(gspca_dev) -#define gspca_input_destroy_urb(gspca_dev) +static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) +{ +} + +static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev) +{ +} + +static inline int gspca_input_connect(struct gspca_dev *dev) +{ + return 0; +} #endif /* get the current input frame buffer */ @@ -442,8 +451,7 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, * is not queued, discard the whole frame */ if (packet_type == FIRST_PACKET) { frame->data_end = frame->data; - jiffies_to_timeval(get_jiffies_64(), - &frame->v4l2_buf.timestamp); + frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get()); frame->v4l2_buf.sequence = ++gspca_dev->sequence; } else if (gspca_dev->last_packet_type == DISCARD_PACKET) { if (packet_type == LAST_PACKET) @@ -605,6 +613,37 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) } } +static int gspca_set_alt0(struct gspca_dev *gspca_dev) +{ + int ret; + + if (gspca_dev->alt == 0) + return 0; + ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); + if (ret < 0) + PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret); + return ret; +} + +/* Note: both the queue and the usb locks should be held when calling this */ +static void gspca_stream_off(struct gspca_dev *gspca_dev) +{ + gspca_dev->streaming = 0; + if (gspca_dev->present) { + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + gspca_input_create_urb(gspca_dev); + } + + /* always call stop0 to free the subdriver's resources */ + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + PDEBUG(D_STREAM, "stream off OK"); +} + /* * look for an input transfer endpoint in an alternate setting */ @@ -830,8 +869,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) } if (ret >= 0) break; - gspca_dev->streaming = 0; - destroy_urbs(gspca_dev); + gspca_stream_off(gspca_dev); if (ret != -ENOSPC) { PDEBUG(D_ERR|D_STREAM, "usb_submit_urb alt %d err %d", @@ -861,37 +899,6 @@ out: return ret; } -static int gspca_set_alt0(struct gspca_dev *gspca_dev) -{ - int ret; - - if (gspca_dev->alt == 0) - return 0; - ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); - if (ret < 0) - PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret); - return ret; -} - -/* Note: both the queue and the usb locks should be held when calling this */ -static void gspca_stream_off(struct gspca_dev *gspca_dev) -{ - gspca_dev->streaming = 0; - if (gspca_dev->present) { - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - gspca_input_create_urb(gspca_dev); - } - - /* always call stop0 to free the subdriver's resources */ - if (gspca_dev->sd_desc->stop0) - gspca_dev->sd_desc->stop0(gspca_dev); - PDEBUG(D_STREAM, "stream off OK"); -} - static void gspca_set_default_mode(struct gspca_dev *gspca_dev) { int i; @@ -1495,7 +1502,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) { struct gspca_dev *gspca_dev = priv; - int i, ret = 0; + int i, ret = 0, streaming; switch (rb->memory) { case GSPCA_MEMORY_READ: /* (internal call) */ @@ -1530,7 +1537,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, } /* stop streaming */ - if (gspca_dev->streaming) { + streaming = gspca_dev->streaming; + if (streaming) { mutex_lock(&gspca_dev->usb_lock); gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); @@ -1549,6 +1557,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, if (ret == 0) { rb->count = gspca_dev->nframes; gspca_dev->capt_file = file; + if (streaming) + ret = gspca_init_transfer(gspca_dev); } out: mutex_unlock(&gspca_dev->queue_lock); @@ -1582,6 +1592,12 @@ static int vidioc_streamon(struct file *file, void *priv, if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; + /* check the capture file */ + if (gspca_dev->capt_file != file) { + ret = -EBUSY; + goto out; + } + if (gspca_dev->nframes == 0 || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { ret = -EINVAL; @@ -1619,6 +1635,12 @@ static int vidioc_streamoff(struct file *file, void *priv, if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; + /* check the capture file */ + if (gspca_dev->capt_file != file) { + ret = -EBUSY; + goto out; + } + /* stop streaming */ if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { ret = -ERESTARTSYS; @@ -2124,7 +2146,7 @@ static ssize_t dev_read(struct file *file, char __user *data, } /* get a frame */ - jiffies_to_timeval(get_jiffies_64(), ×tamp); + timestamp = ktime_to_timeval(ktime_get()); timestamp.tv_sec--; n = 2; for (;;) { @@ -2315,7 +2337,7 @@ int gspca_dev_probe(struct usb_interface *intf, return 0; out: -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) if (gspca_dev->input_dev) input_unregister_device(gspca_dev->input_dev); #endif @@ -2334,7 +2356,7 @@ EXPORT_SYMBOL(gspca_dev_probe); void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct input_dev *input_dev; #endif @@ -2348,7 +2370,7 @@ void gspca_disconnect(struct usb_interface *intf) wake_up_interruptible(&gspca_dev->wq); } -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) gspca_input_destroy_urb(gspca_dev); input_dev = gspca_dev->input_dev; if (input_dev) { diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 8bb242fb79de..8b963dfae861 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -130,7 +130,7 @@ struct sd_desc { cam_reg_op get_register; #endif cam_ident_op get_chip_ident; -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) cam_int_pkt_op int_pkt_scan; /* other_input makes the gspca core create gspca_dev->input even when int_pkt_scan is NULL, for cams with non interrupt driven buttons */ @@ -158,7 +158,7 @@ struct gspca_dev { struct module *module; /* subdriver handling the device */ struct usb_device *dev; struct file *capt_file; /* file doing video capture */ -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct input_dev *input_dev; char phys[64]; /* physical device path */ #endif @@ -171,7 +171,7 @@ struct gspca_dev { #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ struct urb *urb[MAX_NURBS]; -#ifdef CONFIG_INPUT +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct urb *int_urb; #endif diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 957e05e2d08f..dc1e4efe30fb 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -10,8 +10,8 @@ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ * * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr - * PS3 Eye camera, brightness, contrast, hue, AWB control added - * by Max Thrun <bear24rw@gmail.com> + * PS3 Eye camera - brightness, contrast, awb, agc, aec controls + * added by Max Thrun <bear24rw@gmail.com> * * 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 @@ -60,15 +60,13 @@ struct sd { u8 contrast; u8 gain; u8 exposure; - u8 redblc; - u8 blueblc; - u8 hue; - u8 autogain; + u8 agc; u8 awb; + u8 aec; s8 sharpness; u8 hflip; u8 vflip; - + u8 freqfltr; }; /* V4L2 controls supported by the driver */ @@ -76,197 +74,183 @@ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setagc(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getagc(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); -static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val); static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setaec(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getaec(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreqfltr(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreqfltr(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu); static const struct ctrl sd_ctrls[] = { - { /* 0 */ - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, -#define BRIGHTNESS_DEF 20 - .default_value = BRIGHTNESS_DEF, + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 0 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, - { /* 1 */ - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, -#define CONTRAST_DEF 37 - .default_value = CONTRAST_DEF, + { /* 1 */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_DEF 32 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, }, - .set = sd_setcontrast, - .get = sd_getcontrast, - }, - { /* 2 */ - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Main Gain", - .minimum = 0, - .maximum = 63, - .step = 1, + { /* 2 */ + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Main Gain", + .minimum = 0, + .maximum = 63, + .step = 1, #define GAIN_DEF 20 - .default_value = GAIN_DEF, + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, }, - .set = sd_setgain, - .get = sd_getgain, - }, - { /* 3 */ - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 255, - .step = 1, + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 255, + .step = 1, #define EXPO_DEF 120 - .default_value = EXPO_DEF, - }, - .set = sd_setexposure, - .get = sd_getexposure, - }, - { /* 4 */ - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 255, - .step = 1, -#define RED_BALANCE_DEF 128 - .default_value = RED_BALANCE_DEF, + .default_value = EXPO_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, }, - .set = sd_setredblc, - .get = sd_getredblc, - }, - { /* 5 */ - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 255, - .step = 1, -#define BLUE_BALANCE_DEF 128 - .default_value = BLUE_BALANCE_DEF, + { /* 4 */ + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AGC_DEF 1 + .default_value = AGC_DEF, + }, + .set = sd_setagc, + .get = sd_getagc, }, - .set = sd_setblueblc, - .get = sd_getblueblc, - }, - { /* 6 */ - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = 0, - .maximum = 255, - .step = 1, -#define HUE_DEF 143 - .default_value = HUE_DEF, +#define AWB_IDX 5 + { /* 5 */ + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AWB_DEF 1 + .default_value = AWB_DEF, + }, + .set = sd_setawb, + .get = sd_getawb, }, - .set = sd_sethue, - .get = sd_gethue, - }, - { /* 7 */ - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Autogain", - .minimum = 0, - .maximum = 1, - .step = 1, -#define AUTOGAIN_DEF 0 - .default_value = AUTOGAIN_DEF, + { /* 6 */ + { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AEC_DEF 1 + .default_value = AEC_DEF, + }, + .set = sd_setaec, + .get = sd_getaec, }, - .set = sd_setautogain, - .get = sd_getautogain, - }, -#define AWB_IDX 8 - { /* 8 */ - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto White Balance", - .minimum = 0, - .maximum = 1, - .step = 1, -#define AWB_DEF 0 - .default_value = AWB_DEF, - }, - .set = sd_setawb, - .get = sd_getawb, - }, - { /* 9 */ - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 63, - .step = 1, + { /* 7 */ + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 63, + .step = 1, #define SHARPNESS_DEF 0 - .default_value = SHARPNESS_DEF, + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, }, - .set = sd_setsharpness, - .get = sd_getsharpness, - }, - { /* 10 */ - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "HFlip", - .minimum = 0, - .maximum = 1, - .step = 1, + { /* 8 */ + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "HFlip", + .minimum = 0, + .maximum = 1, + .step = 1, #define HFLIP_DEF 0 - .default_value = HFLIP_DEF, + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, }, - .set = sd_sethflip, - .get = sd_gethflip, - }, - { /* 11 */ - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "VFlip", - .minimum = 0, - .maximum = 1, - .step = 1, + { /* 9 */ + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "VFlip", + .minimum = 0, + .maximum = 1, + .step = 1, #define VFLIP_DEF 0 - .default_value = VFLIP_DEF, + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, + { /* 10 */ + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light Frequency Filter", + .minimum = 0, + .maximum = 1, + .step = 1, +#define FREQFLTR_DEF 0 + .default_value = FREQFLTR_DEF, + }, + .set = sd_setfreqfltr, + .get = sd_getfreqfltr, }, - .set = sd_setvflip, - .get = sd_getvflip, - }, }; static const struct v4l2_pix_format ov772x_mode[] = { @@ -675,14 +659,14 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sccb_reg_write(gspca_dev, 0x9B, sd->brightness); + sccb_reg_write(gspca_dev, 0x9b, sd->brightness); } static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sccb_reg_write(gspca_dev, 0x9C, sd->contrast); + sccb_reg_write(gspca_dev, 0x9c, sd->contrast); } static void setgain(struct gspca_dev *gspca_dev) @@ -690,6 +674,9 @@ static void setgain(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; + if (sd->agc) + return; + val = sd->gain; switch (val & 0x30) { case 0x00: @@ -717,55 +704,68 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; u8 val; + if (sd->aec) + return; + + /* 'val' is one byte and represents half of the exposure value we are + * going to set into registers, a two bytes value: + * + * MSB: ((u16) val << 1) >> 8 == val >> 7 + * LSB: ((u16) val << 1) & 0xff == val << 1 + */ val = sd->exposure; sccb_reg_write(gspca_dev, 0x08, val >> 7); sccb_reg_write(gspca_dev, 0x10, val << 1); } -static void setredblc(struct gspca_dev *gspca_dev) +static void setagc(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sccb_reg_write(gspca_dev, 0x43, sd->redblc); -} - -static void setblueblc(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sccb_reg_write(gspca_dev, 0x42, sd->blueblc); -} - -static void sethue(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; + if (sd->agc) { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | 0x04); + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) | 0x03); + } else { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~0x04); + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) & ~0x03); - sccb_reg_write(gspca_dev, 0x01, sd->hue); + setgain(gspca_dev); + } } -static void setautogain(struct gspca_dev *gspca_dev) +static void setawb(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->autogain) { - sccb_reg_write(gspca_dev, 0x13, 0xf7); /* AGC,AEC,AWB ON */ - sccb_reg_write(gspca_dev, 0x64, - sccb_reg_read(gspca_dev, 0x64) | 0x03); + if (sd->awb) { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | 0x02); + sccb_reg_write(gspca_dev, 0x63, + sccb_reg_read(gspca_dev, 0x63) | 0xc0); } else { - sccb_reg_write(gspca_dev, 0x13, 0xf0); /* AGC,AEC,AWB OFF */ - sccb_reg_write(gspca_dev, 0x64, - sccb_reg_read(gspca_dev, 0x64) & 0xfc); + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~0x02); + sccb_reg_write(gspca_dev, 0x63, + sccb_reg_read(gspca_dev, 0x63) & ~0xc0); } } -static void setawb(struct gspca_dev *gspca_dev) +static void setaec(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->awb) - sccb_reg_write(gspca_dev, 0x63, 0xe0); /* AWB on */ - else - sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */ + if (sd->aec) + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) | 0x01); + else { + sccb_reg_write(gspca_dev, 0x13, + sccb_reg_read(gspca_dev, 0x13) & ~0x01); + setexposure(gspca_dev); + } } static void setsharpness(struct gspca_dev *gspca_dev) @@ -774,8 +774,8 @@ static void setsharpness(struct gspca_dev *gspca_dev) u8 val; val = sd->sharpness; - sccb_reg_write(gspca_dev, 0x91, val); /* vga noise */ - sccb_reg_write(gspca_dev, 0x8e, val); /* qvga noise */ + sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */ + sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */ } static void sethflip(struct gspca_dev *gspca_dev) @@ -787,7 +787,7 @@ static void sethflip(struct gspca_dev *gspca_dev) sccb_reg_read(gspca_dev, 0x0c) | 0x40); else sccb_reg_write(gspca_dev, 0x0c, - sccb_reg_read(gspca_dev, 0x0c) & 0xbf); + sccb_reg_read(gspca_dev, 0x0c) & ~0x40); } static void setvflip(struct gspca_dev *gspca_dev) @@ -799,9 +799,20 @@ static void setvflip(struct gspca_dev *gspca_dev) sccb_reg_read(gspca_dev, 0x0c) | 0x80); else sccb_reg_write(gspca_dev, 0x0c, - sccb_reg_read(gspca_dev, 0x0c) & 0x7f); + sccb_reg_read(gspca_dev, 0x0c) & ~0x80); } +static void setfreqfltr(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->freqfltr == 0) + sccb_reg_write(gspca_dev, 0x2b, 0x00); + else + sccb_reg_write(gspca_dev, 0x2b, 0x9e); +} + + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -825,26 +836,17 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->contrast = CONTRAST_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPO_DEF; - sd->redblc = RED_BALANCE_DEF; - sd->blueblc = BLUE_BALANCE_DEF; - sd->hue = HUE_DEF; -#if AUTOGAIN_DEF != 0 - sd->autogain = AUTOGAIN_DEF; +#if AGC_DEF != 0 + sd->agc = AGC_DEF; #else gspca_dev->ctrl_inac |= (1 << AWB_IDX); #endif -#if AWB_DEF != 0 - sd->awb = AWB_DEF -#endif -#if SHARPNESS_DEF != 0 + sd->awb = AWB_DEF; + sd->aec = AEC_DEF; sd->sharpness = SHARPNESS_DEF; -#endif -#if HFLIP_DEF != 0 sd->hflip = HFLIP_DEF; -#endif -#if VFLIP_DEF != 0 sd->vflip = VFLIP_DEF; -#endif + sd->freqfltr = FREQFLTR_DEF; return 0; } @@ -904,18 +906,17 @@ static int sd_start(struct gspca_dev *gspca_dev) } set_frame_rate(gspca_dev); - setautogain(gspca_dev); + setagc(gspca_dev); setawb(gspca_dev); + setaec(gspca_dev); setgain(gspca_dev); - setredblc(gspca_dev); - setblueblc(gspca_dev); - sethue(gspca_dev); setexposure(gspca_dev); setbrightness(gspca_dev); setcontrast(gspca_dev); setsharpness(gspca_dev); setvflip(gspca_dev); sethflip(gspca_dev); + setfreqfltr(gspca_dev); ov534_set_led(gspca_dev, 1); ov534_reg_write(gspca_dev, 0xe0, 0x00); @@ -1092,65 +1093,11 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->redblc = val; - if (gspca_dev->streaming) - setredblc(gspca_dev); - return 0; -} - -static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->redblc; - return 0; -} - -static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->blueblc = val; - if (gspca_dev->streaming) - setblueblc(gspca_dev); - return 0; -} - -static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_setagc(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->blueblc; - return 0; -} - -static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hue = val; - if (gspca_dev->streaming) - sethue(gspca_dev); - return 0; -} - -static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->hue; - return 0; -} - -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; + sd->agc = val; if (gspca_dev->streaming) { @@ -1160,16 +1107,16 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) gspca_dev->ctrl_inac &= ~(1 << AWB_IDX); else gspca_dev->ctrl_inac |= (1 << AWB_IDX); - setautogain(gspca_dev); + setagc(gspca_dev); } return 0; } -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getagc(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->autogain; + *val = sd->agc; return 0; } @@ -1191,6 +1138,24 @@ static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setaec(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->aec = val; + if (gspca_dev->streaming) + setaec(gspca_dev); + return 0; +} + +static int sd_getaec(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->aec; + return 0; +} + static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1245,6 +1210,43 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreqfltr(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freqfltr = val; + if (gspca_dev->streaming) + setfreqfltr(gspca_dev); + return 0; +} + +static int sd_getfreqfltr(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freqfltr; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "Disabled"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + } + break; + } + + return -EINVAL; +} + /* get stream parameters (framerate) */ static int sd_get_streamparm(struct gspca_dev *gspca_dev, struct v4l2_streamparm *parm) @@ -1296,6 +1298,7 @@ static const struct sd_desc sd_desc = { .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, .get_streamparm = sd_get_streamparm, .set_streamparm = sd_set_streamparm, }; diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 0c87c3490b1e..a40f8893310d 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -99,7 +99,7 @@ static const struct ctrl sd_ctrls[] = { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", + .name = "Exposure", .minimum = PAC207_EXPOSURE_MIN, .maximum = PAC207_EXPOSURE_MAX, .step = 1, @@ -130,7 +130,7 @@ static const struct ctrl sd_ctrls[] = { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", + .name = "Gain", .minimum = PAC207_GAIN_MIN, .maximum = PAC207_GAIN_MAX, .step = 1, diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c index dda5fd4aa69e..71d9447a7986 100644 --- a/drivers/media/video/gspca/sn9c2028.c +++ b/drivers/media/video/gspca/sn9c2028.c @@ -39,7 +39,7 @@ struct init_command { }; /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { }; /* How to change the resolution of any of the VGA cams is unknown */ diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 3dee3e5844b6..644a7fd4701a 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -18,10 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV -#include <linux/kthread.h> -#include <linux/freezer.h> -#include <linux/usb/input.h> +#ifdef CONFIG_INPUT #include <linux/input.h> #include <linux/slab.h> #endif @@ -30,6 +27,7 @@ #include "jpeg.h" #include <media/v4l2-chip-ident.h> +#include <linux/dmi.h> MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, " "microdia project <microdia@googlegroups.com>"); @@ -52,9 +50,15 @@ MODULE_LICENSE("GPL"); #define SENSOR_MT9V112 7 #define SENSOR_MT9M001 8 #define SENSOR_MT9M111 9 -#define SENSOR_HV7131R 10 +#define SENSOR_MT9M112 10 +#define SENSOR_HV7131R 11 #define SENSOR_MT9VPRB 20 +/* camera flags */ +#define HAS_NO_BUTTON 0x1 +#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ +#define FLIP_DETECT 0x4 + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; @@ -88,11 +92,7 @@ struct sd { u8 *jpeg_hdr; u8 quality; -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV - struct input_dev *input_dev; - u8 input_gpio; - struct task_struct *input_task; -#endif + u8 flags; }; struct i2c_reg_u8 { @@ -130,6 +130,39 @@ static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val); static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val); static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val); +static const struct dmi_system_id flip_dmi_table[] = { + { + .ident = "MSI MS-1034", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0341") + } + }, + { + .ident = "MSI MS-1632", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1632") + } + }, + { + .ident = "MSI MS-1635X", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1635X") + } + }, + { + .ident = "ASUSTeK W7J", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_BOARD_NAME, "W7J ") + } + }, + {} +}; + static const struct ctrl sd_ctrls[] = { { #define BRIGHTNESS_IDX 0 @@ -713,6 +746,7 @@ static u16 i2c_ident[] = { V4L2_IDENT_MT9V112, V4L2_IDENT_MT9M001C12ST, V4L2_IDENT_MT9M111, + V4L2_IDENT_MT9M112, V4L2_IDENT_HV7131R, }; @@ -735,7 +769,8 @@ static u16 bridge_init[][2] = { {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f}, {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f}, {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01}, - {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80} + {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80}, + {0x1007, 0x00} }; /* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ @@ -914,40 +949,30 @@ static struct i2c_reg_u8 ov9650_init[] = { }; static struct i2c_reg_u8 ov9655_init[] = { - {0x12, 0x80}, {0x12, 0x01}, {0x0d, 0x00}, {0x0e, 0x61}, - {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, - {0x1e, 0x04}, {0x1e, 0x04}, {0x1e, 0x04}, {0x27, 0x08}, - {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x32, 0xbf}, - {0x34, 0x3d}, {0x35, 0x00}, {0x36, 0xf8}, {0x38, 0x12}, - {0x39, 0x57}, {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, - {0x3d, 0x19}, {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, - {0x42, 0x80}, {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, - {0x48, 0x3c}, {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, - {0x4d, 0xdc}, {0x4e, 0xdc}, {0x69, 0x02}, {0x6c, 0x04}, - {0x6f, 0x9e}, {0x70, 0x05}, {0x71, 0x78}, {0x77, 0x02}, - {0x8a, 0x23}, {0x8c, 0x0d}, {0x90, 0x7e}, {0x91, 0x7c}, - {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, {0xa6, 0x60}, - {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, {0xab, 0x04}, - {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, {0xaf, 0x80}, - {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, {0xb6, 0xaf}, - {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, {0xbe, 0x3b}, - {0xbf, 0x3a}, {0xc0, 0xe2}, {0xc1, 0xc8}, {0xc2, 0x01}, + {0x12, 0x80}, {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, + {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08}, + {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d}, + {0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57}, + {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, {0x3d, 0x19}, + {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, {0x42, 0x80}, + {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, {0x48, 0x3c}, + {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, {0x4d, 0xdc}, + {0x4e, 0xdc}, {0x6c, 0x04}, {0x6f, 0x9e}, {0x70, 0x05}, + {0x71, 0x78}, {0x77, 0x02}, {0x8a, 0x23}, {0x90, 0x7e}, + {0x91, 0x7c}, {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68}, + {0xa6, 0x60}, {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92}, + {0xab, 0x04}, {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80}, + {0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00}, + {0xb6, 0xaf}, {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44}, + {0xbe, 0x3b}, {0xbf, 0x3a}, {0xc1, 0xc8}, {0xc2, 0x01}, {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0}, - {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x12, 0x61}, + {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x2d, 0x00}, + {0x2e, 0x00}, {0x01, 0x80}, {0x02, 0x80}, {0x12, 0x61}, {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a}, - {0x03, 0x12}, {0x17, 0x14}, {0x18, 0x00}, {0x19, 0x01}, - {0x1a, 0x3d}, {0x32, 0xbf}, {0x11, 0x80}, {0x2a, 0x10}, - {0x2b, 0x0a}, {0x92, 0x00}, {0x93, 0x00}, {0x1e, 0x04}, - {0x1e, 0x04}, {0x10, 0x7c}, {0x04, 0x03}, {0xa1, 0x00}, - {0x2d, 0x00}, {0x2e, 0x00}, {0x00, 0x00}, {0x01, 0x80}, - {0x02, 0x80}, {0x12, 0x61}, {0x36, 0xfa}, {0x8c, 0x8d}, - {0xc0, 0xaa}, {0x69, 0x0a}, {0x03, 0x12}, {0x17, 0x14}, - {0x18, 0x00}, {0x19, 0x01}, {0x1a, 0x3d}, {0x32, 0xbf}, - {0x11, 0x80}, {0x2a, 0x10}, {0x2b, 0x0a}, {0x92, 0x00}, - {0x93, 0x00}, {0x04, 0x01}, {0x10, 0x1f}, {0xa1, 0x00}, - {0x00, 0x0a}, {0xa1, 0x00}, {0x10, 0x5d}, {0x04, 0x03}, - {0x00, 0x01}, {0xa1, 0x00}, {0x10, 0x7c}, {0x04, 0x03}, - {0x00, 0x03}, {0x00, 0x0a}, {0x00, 0x10}, {0x00, 0x13}, + {0x03, 0x09}, {0x17, 0x16}, {0x18, 0x6e}, {0x19, 0x01}, + {0x1a, 0x3e}, {0x32, 0x09}, {0x2a, 0x10}, {0x2b, 0x0a}, + {0x92, 0x00}, {0x93, 0x00}, {0xa1, 0x00}, {0x10, 0x7c}, + {0x04, 0x03}, {0x00, 0x13}, }; static struct i2c_reg_u16 mt9v112_init[] = { @@ -971,29 +996,12 @@ static struct i2c_reg_u16 mt9v112_init[] = { static struct i2c_reg_u16 mt9v111_init[] = { {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, - {0x01, 0x0001}, {0x02, 0x0016}, {0x03, 0x01e1}, - {0x04, 0x0281}, {0x05, 0x0004}, {0x07, 0x3002}, - {0x21, 0x0000}, {0x25, 0x4024}, {0x26, 0xff03}, - {0x27, 0xff10}, {0x2b, 0x7828}, {0x2c, 0xb43c}, - {0x2d, 0xf0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064}, - {0x67, 0x4010}, {0x06, 0x301e}, {0x08, 0x0480}, - {0x01, 0x0004}, {0x02, 0x0016}, {0x03, 0x01e6}, - {0x04, 0x0286}, {0x05, 0x0004}, {0x06, 0x0000}, - {0x07, 0x3002}, {0x08, 0x0008}, {0x0c, 0x0000}, - {0x0d, 0x0000}, {0x0e, 0x0000}, {0x0f, 0x0000}, - {0x10, 0x0000}, {0x11, 0x0000}, {0x12, 0x00b0}, - {0x13, 0x007c}, {0x14, 0x0000}, {0x15, 0x0000}, - {0x16, 0x0000}, {0x17, 0x0000}, {0x18, 0x0000}, - {0x19, 0x0000}, {0x1a, 0x0000}, {0x1b, 0x0000}, - {0x1c, 0x0000}, {0x1d, 0x0000}, {0x30, 0x0000}, - {0x30, 0x0005}, {0x31, 0x0000}, {0x02, 0x0016}, - {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0004}, - {0x06, 0x0000}, {0x07, 0x3002}, {0x06, 0x002d}, - {0x05, 0x0004}, {0x09, 0x0064}, {0x2b, 0x00a0}, - {0x2c, 0x00a0}, {0x2d, 0x00a0}, {0x2e, 0x00a0}, - {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, - {0x05, 0x0004}, {0x06, 0x002d}, {0x07, 0x3002}, - {0x0e, 0x0008}, {0x06, 0x002d}, {0x05, 0x0004}, + {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0}, + {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e}, + {0x08, 0x0480}, {0x01, 0x0004}, {0x02, 0x0016}, + {0x03, 0x01e7}, {0x04, 0x0287}, {0x05, 0x0004}, + {0x06, 0x002d}, {0x07, 0x3002}, {0x08, 0x0008}, + {0x0e, 0x0008}, {0x20, 0x0000} }; static struct i2c_reg_u16 mt9v011_init[] = { @@ -1043,6 +1051,13 @@ static struct i2c_reg_u16 mt9m111_init[] = { {0xf0, 0x0000}, }; +static struct i2c_reg_u16 mt9m112_init[] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, + {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, + {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, + {0xf0, 0x0000}, +}; + static struct i2c_reg_u8 hv7131r_init[] = { {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, @@ -1240,8 +1255,8 @@ static int ov9655_init_sensor(struct gspca_dev *gspca_dev) } /* disable hflip and vflip */ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); - sd->hstart = 0; - sd->vstart = 7; + sd->hstart = 1; + sd->vstart = 2; return 0; } @@ -1337,6 +1352,7 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) return -ENODEV; } } + gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX); sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V111; @@ -1369,6 +1385,23 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) return -ENODEV; } +static int mt9m112_init_sensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + for (i = 0; i < ARRAY_SIZE(mt9m112_init); i++) { + if (i2c_w2(gspca_dev, mt9m112_init[i].reg, + mt9m112_init[i].val) < 0) { + err("MT9M112 sensor initialization failed"); + return -ENODEV; + } + } + gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX); + sd->hstart = 0; + sd->vstart = 2; + return 0; +} + static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1421,87 +1454,6 @@ static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) return 0; } -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV -static int input_kthread(void *data) -{ - struct gspca_dev *gspca_dev = (struct gspca_dev *)data; - struct sd *sd = (struct sd *) gspca_dev; - - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait); - set_freezable(); - for (;;) { - if (kthread_should_stop()) - break; - - if (reg_r(gspca_dev, 0x1005, 1) < 0) - continue; - - input_report_key(sd->input_dev, - KEY_CAMERA, - gspca_dev->usb_buf[0] & sd->input_gpio); - input_sync(sd->input_dev); - - wait_event_freezable_timeout(wait, - kthread_should_stop(), - msecs_to_jiffies(100)); - } - return 0; -} - - -static int sn9c20x_input_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - if (sd->input_gpio == 0) - return 0; - - sd->input_dev = input_allocate_device(); - if (!sd->input_dev) - return -ENOMEM; - - sd->input_dev->name = "SN9C20X Webcam"; - - sd->input_dev->phys = kasprintf(GFP_KERNEL, "usb-%s-%s", - gspca_dev->dev->bus->bus_name, - gspca_dev->dev->devpath); - - if (!sd->input_dev->phys) - return -ENOMEM; - - usb_to_input_id(gspca_dev->dev, &sd->input_dev->id); - sd->input_dev->dev.parent = &gspca_dev->dev->dev; - - set_bit(EV_KEY, sd->input_dev->evbit); - set_bit(KEY_CAMERA, sd->input_dev->keybit); - - if (input_register_device(sd->input_dev)) - return -EINVAL; - - sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%s-%s", - gspca_dev->dev->bus->bus_name, - gspca_dev->dev->devpath); - - if (IS_ERR(sd->input_task)) - return -EINVAL; - - return 0; -} - -static void sn9c20x_input_cleanup(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - if (sd->input_task != NULL && !IS_ERR(sd->input_task)) - kthread_stop(sd->input_task); - - if (sd->input_dev != NULL) { - input_unregister_device(sd->input_dev); - kfree(sd->input_dev->phys); - input_free_device(sd->input_dev); - sd->input_dev = NULL; - } -} -#endif - static int set_cmatrix(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1579,17 +1531,26 @@ static int set_redblue(struct gspca_dev *gspca_dev) static int set_hvflip(struct gspca_dev *gspca_dev) { - u8 value, tslb; + u8 value, tslb, hflip, vflip; u16 value2; struct sd *sd = (struct sd *) gspca_dev; + + if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) { + hflip = !sd->hflip; + vflip = !sd->vflip; + } else { + hflip = sd->hflip; + vflip = sd->vflip; + } + switch (sd->sensor) { case SENSOR_OV9650: i2c_r1(gspca_dev, 0x1e, &value); value &= ~0x30; tslb = 0x01; - if (sd->hflip) + if (hflip) value |= 0x20; - if (sd->vflip) { + if (vflip) { value |= 0x10; tslb = 0x49; } @@ -1600,28 +1561,29 @@ static int set_hvflip(struct gspca_dev *gspca_dev) case SENSOR_MT9V011: i2c_r2(gspca_dev, 0x20, &value2); value2 &= ~0xc0a0; - if (sd->hflip) + if (hflip) value2 |= 0x8080; - if (sd->vflip) + if (vflip) value2 |= 0x4020; i2c_w2(gspca_dev, 0x20, value2); break; + case SENSOR_MT9M112: case SENSOR_MT9M111: case SENSOR_MT9V112: i2c_r2(gspca_dev, 0x20, &value2); value2 &= ~0x0003; - if (sd->hflip) + if (hflip) value2 |= 0x0002; - if (sd->vflip) + if (vflip) value2 |= 0x0001; i2c_w2(gspca_dev, 0x20, value2); break; case SENSOR_HV7131R: i2c_r1(gspca_dev, 0x01, &value); value &= ~0x03; - if (sd->vflip) + if (vflip) value |= 0x01; - if (sd->hflip) + if (hflip) value |= 0x02; i2c_w1(gspca_dev, 0x01, value); break; @@ -1645,7 +1607,6 @@ static int set_exposure(struct gspca_dev *gspca_dev) break; case SENSOR_MT9M001: case SENSOR_MT9V112: - case SENSOR_MT9V111: case SENSOR_MT9V011: exp[0] |= (3 << 4); exp[2] = 0x09; @@ -1655,9 +1616,9 @@ static int set_exposure(struct gspca_dev *gspca_dev) case SENSOR_HV7131R: exp[0] |= (4 << 4); exp[2] = 0x25; - exp[3] = ((sd->exposure * 0xffffff) / 0xffff) >> 16; - exp[4] = ((sd->exposure * 0xffffff) / 0xffff) >> 8; - exp[5] = ((sd->exposure * 0xffffff) / 0xffff) & 0xff; + exp[3] = (sd->exposure >> 5) & 0xff; + exp[4] = (sd->exposure << 3) & 0xff; + exp[5] = 0; break; default: return 0; @@ -1680,7 +1641,6 @@ static int set_gain(struct gspca_dev *gspca_dev) gain[3] = ov_gain[sd->gain]; break; case SENSOR_MT9V011: - case SENSOR_MT9V111: gain[0] |= (3 << 4); gain[2] = 0x35; gain[3] = micron1_gain[sd->gain] >> 8; @@ -1931,7 +1891,7 @@ static int sd_dbg_g_register(struct gspca_dev *gspca_dev, if (reg->match.addr != sd->i2c_addr) return -EINVAL; if (sd->sensor >= SENSOR_MT9V011 && - sd->sensor <= SENSOR_MT9M111) { + sd->sensor <= SENSOR_MT9M112) { if (i2c_r2(gspca_dev, reg->reg, (u16 *)®->val) < 0) return -EINVAL; } else { @@ -1960,7 +1920,7 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, if (reg->match.addr != sd->i2c_addr) return -EINVAL; if (sd->sensor >= SENSOR_MT9V011 && - sd->sensor <= SENSOR_MT9M111) { + sd->sensor <= SENSOR_MT9M112) { if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0) return -EINVAL; } else { @@ -2005,8 +1965,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = (id->driver_info >> 8) & 0xff; sd->i2c_addr = id->driver_info & 0xff; + sd->flags = (id->driver_info >> 16) & 0xff; switch (sd->sensor) { + case SENSOR_MT9M112: case SENSOR_MT9M111: case SENSOR_OV9650: case SENSOR_SOI968: @@ -2039,11 +2001,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->quality = 95; -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV - sd->input_gpio = (id->driver_info >> 16) & 0xff; - if (sn9c20x_input_init(gspca_dev) < 0) - return -ENODEV; -#endif return 0; } @@ -2063,6 +2020,11 @@ static int sd_init(struct gspca_dev *gspca_dev) } } + if (sd->flags & LED_REVERSE) + reg_w1(gspca_dev, 0x1006, 0x00); + else + reg_w1(gspca_dev, 0x1006, 0x20); + if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) { err("Device initialization failed"); return -ENODEV; @@ -2103,6 +2065,11 @@ static int sd_init(struct gspca_dev *gspca_dev) return -ENODEV; info("MT9M111 sensor detected"); break; + case SENSOR_MT9M112: + if (mt9m112_init_sensor(gspca_dev) < 0) + return -ENODEV; + info("MT9M112 sensor detected"); + break; case SENSOR_MT9M001: if (mt9m001_init_sensor(gspca_dev) < 0) return -ENODEV; @@ -2162,6 +2129,7 @@ static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); } break; + case SENSOR_MT9M112: case SENSOR_MT9M111: if (mode & MODE_SXGA) { i2c_w2(gspca_dev, 0xf0, 0x0002); @@ -2243,6 +2211,8 @@ static int sd_start(struct gspca_dev *gspca_dev) set_exposure(gspca_dev); set_hvflip(gspca_dev); + reg_w1(gspca_dev, 0x1007, 0x20); + reg_r(gspca_dev, 0x1061, 1); reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); return 0; @@ -2250,6 +2220,8 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { + reg_w1(gspca_dev, 0x1007, 0x00); + reg_r(gspca_dev, 0x1061, 1); reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); } @@ -2343,6 +2315,24 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) do_autoexposure(gspca_dev, avg_lum); } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet */ + int len) /* interrupt packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = -EINVAL; + if (!(sd->flags & HAS_NO_BUTTON) && len == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + return ret; +} +#endif + static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -2409,6 +2399,9 @@ static const struct sd_desc sd_desc = { .stopN = sd_stopN, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif .dq_callback = sd_dqcallback, #ifdef CONFIG_VIDEO_ADV_DEBUG .set_register = sd_dbg_s_register, @@ -2417,8 +2410,8 @@ static const struct sd_desc sd_desc = { .get_chip_ident = sd_chip_ident, }; -#define SN9C20X(sensor, i2c_addr, button_mask) \ - .driver_info = (button_mask << 16) \ +#define SN9C20X(sensor, i2c_addr, flags) \ + .driver_info = ((flags & 0xff) << 16) \ | (SENSOR_ ## sensor << 8) \ | (i2c_addr) @@ -2426,8 +2419,10 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)}, - {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, 0x10)}, - {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x624c), SN9C20X(MT9M112, 0x5d, 0)}, + {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, LED_REVERSE)}, + {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, + (FLIP_DETECT | HAS_NO_BUTTON))}, {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)}, @@ -2438,6 +2433,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)}, + {USB_DEVICE(0x0c45, 0x628c), SN9C20X(MT9M112, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)}, @@ -2448,6 +2444,8 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)}, {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)}, {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)}, + {USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)}, + {USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)}, {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)}, {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)}, {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)}, @@ -2467,22 +2465,11 @@ static int sd_probe(struct usb_interface *intf, THIS_MODULE); } -static void sd_disconnect(struct usb_interface *intf) -{ -#ifdef CONFIG_USB_GSPCA_SN9C20X_EVDEV - struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - - sn9c20x_input_cleanup(gspca_dev); -#endif - - gspca_disconnect(intf); -} - static struct usb_driver sd_driver = { .name = MODULE_NAME, .id_table = device_table, .probe = sd_probe, - .disconnect = sd_disconnect, + .disconnect = gspca_disconnect, #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 1d61b92f6bfc..bb923efb75bd 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1,7 +1,7 @@ /* * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver * - * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr> * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify @@ -28,7 +28,7 @@ #define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0) -MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -67,20 +67,25 @@ struct sd { #define BRIDGE_SN9C110 2 #define BRIDGE_SN9C120 3 u8 sensor; /* Type of image sensor chip */ -#define SENSOR_ADCM1700 0 -#define SENSOR_HV7131R 1 -#define SENSOR_MI0360 2 -#define SENSOR_MO4000 3 -#define SENSOR_MT9V111 4 -#define SENSOR_OM6802 5 -#define SENSOR_OV7630 6 -#define SENSOR_OV7648 7 -#define SENSOR_OV7660 8 -#define SENSOR_PO1030 9 -#define SENSOR_SP80708 10 +enum { + SENSOR_ADCM1700, + SENSOR_GC0307, + SENSOR_HV7131R, + SENSOR_MI0360, + SENSOR_MO4000, + SENSOR_MT9V111, + SENSOR_OM6802, + SENSOR_OV7630, + SENSOR_OV7648, + SENSOR_OV7660, + SENSOR_PO1030, + SENSOR_PO2030N, + SENSOR_SOI768, + SENSOR_SP80708, +} sensors; u8 i2c_addr; - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -281,29 +286,60 @@ static const struct ctrl sd_ctrls[] = { }; /* table of the disabled controls */ -static __u32 ctrl_dis[] = { - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX) | - (1 << AUTOGAIN_IDX), /* SENSOR_ADCM1700 0 */ - (1 << INFRARED_IDX) | (1 << FREQ_IDX), - /* SENSOR_HV7131R 1 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MI0360 2 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MO4000 3 */ - (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MT9V111 4 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_OM6802 5 */ - (1 << INFRARED_IDX), - /* SENSOR_OV7630 6 */ - (1 << INFRARED_IDX), - /* SENSOR_OV7648 7 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_OV7660 8 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_PO1030 9 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_SP80708 10 */ +static const __u32 ctrl_dis[] = { +[SENSOR_ADCM1700] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_GC0307] = (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_HV7131R] = (1 << INFRARED_IDX) | + (1 << FREQ_IDX), + +[SENSOR_MI0360] = (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_MO4000] = (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_MT9V111] = (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_OM6802] = (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_OV7630] = (1 << INFRARED_IDX), + +[SENSOR_OV7648] = (1 << INFRARED_IDX), + +[SENSOR_OV7660] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX), + +[SENSOR_PO1030] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_PO2030N] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), +[SENSOR_SOI768] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), + +[SENSOR_SP80708] = (1 << AUTOGAIN_IDX) | + (1 << INFRARED_IDX) | + (1 << VFLIP_IDX) | + (1 << FREQ_IDX), }; static const struct v4l2_pix_format cif_mode[] = { @@ -343,7 +379,17 @@ static const u8 sn_adcm1700[0x1c] = { 0x06, 0x00, 0x00, 0x00 }; -/*Data from sn9c102p+hv7131r */ +static const u8 sn_gc0307[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x61, 0x62, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x03, 0x01, 0x08, 0x28, 0x1e, 0x02, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20, @@ -401,7 +447,7 @@ static const u8 sn_om6802[0x1c] = { static const u8 sn_ov7630[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20, + 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -443,6 +489,28 @@ static const u8 sn_po1030[0x1c] = { 0x07, 0x00, 0x00, 0x00 }; +static const u8 sn_po2030n[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x63, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x01, 0x14, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + +static const u8 sn_soi768[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x01, 0x08, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + static const u8 sn_sp80708[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, @@ -456,17 +524,20 @@ static const u8 sn_sp80708[0x1c] = { /* sequence specific to the sensors - !! index = SENSOR_xxx */ static const u8 *sn_tb[] = { - sn_adcm1700, - sn_hv7131, - sn_mi0360, - sn_mo4000, - sn_mt9v111, - sn_om6802, - sn_ov7630, - sn_ov7648, - sn_ov7660, - sn_po1030, - sn_sp80708 +[SENSOR_ADCM1700] = sn_adcm1700, +[SENSOR_GC0307] = sn_gc0307, +[SENSOR_HV7131R] = sn_hv7131, +[SENSOR_MI0360] = sn_mi0360, +[SENSOR_MO4000] = sn_mo4000, +[SENSOR_MT9V111] = sn_mt9v111, +[SENSOR_OM6802] = sn_om6802, +[SENSOR_OV7630] = sn_ov7630, +[SENSOR_OV7648] = sn_ov7648, +[SENSOR_OV7660] = sn_ov7660, +[SENSOR_PO1030] = sn_po1030, +[SENSOR_PO2030N] = sn_po2030n, +[SENSOR_SOI768] = sn_soi768, +[SENSOR_SP80708] = sn_sp80708, }; /* default gamma table */ @@ -484,8 +555,13 @@ static const u8 gamma_spec_1[17] = { 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d, 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5 }; -/* gamma for sensor SP80708 */ +/* gamma for sensor GC0307 */ static const u8 gamma_spec_2[17] = { + 0x14, 0x37, 0x50, 0x6a, 0x7c, 0x8d, 0x9d, 0xab, + 0xb5, 0xbf, 0xc2, 0xcb, 0xd1, 0xd6, 0xdb, 0xe1, 0xeb +}; +/* gamma for sensor SP80708 */ +static const u8 gamma_spec_3[17] = { 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab, 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6 }; @@ -533,6 +609,58 @@ static const u8 adcm1700_sensor_param1[][8] = { {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10}, {} }; +static const u8 gc0307_sensor_init[][8] = { + {0xa0, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x44, 0xa2, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x02, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x11, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x08, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x09, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0d, 0x22, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/ + {0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x17, 0x52, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x18, 0x50, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1e, 0x0d, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x1f, 0x32, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x61, 0x90, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x63, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x65, 0x98, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x67, 0x90, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x04, 0x96, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x45, 0x27, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x47, 0x2c, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x43, 0x47, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x44, 0xd8, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 gc0307_sensor_param1[][8] = { + {0xa0, 0x21, 0x68, 0x13, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x21, 0x61, 0x80, 0x00, 0x80, 0x00, 0x10}, + {0xc0, 0x21, 0x65, 0x80, 0x00, 0x80, 0x00, 0x10}, + {0xc0, 0x21, 0x63, 0xa0, 0x00, 0xa6, 0x00, 0x10}, +/*param3*/ + {0xa0, 0x21, 0x01, 0x6e, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x02, 0x88, 0x00, 0x00, 0x00, 0x10}, + {} +}; + static const u8 hv7131r_sensor_init[][8] = { {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10}, @@ -767,7 +895,9 @@ static const u8 ov7630_sensor_init[][8] = { {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10}, {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, -/* */ + {} +}; +static const u8 ov7630_sensor_param1[][8] = { {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, /*fixme: + 0x12, 0x04*/ @@ -984,6 +1114,113 @@ static const u8 po1030_sensor_param1[][8] = { {} }; +static const u8 po2030n_sensor_init[][8] = { + {0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */ + {0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */ + {0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x10}, + {0xd1, 0x6e, 0x0c, 0x03, 0x50, 0x01, 0xe8, 0x10}, + {0xd1, 0x6e, 0x1d, 0x20, 0x0a, 0x19, 0x44, 0x10}, + {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x4d, 0x00, 0x00, 0x00, 0xed, 0x10}, + {0xd1, 0x6e, 0x51, 0x17, 0x4a, 0x2f, 0xc0, 0x10}, + {0xd1, 0x6e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x85, 0x00, 0x00, 0x00, 0x08, 0x10}, + {0xd1, 0x6e, 0x89, 0x01, 0xe8, 0x00, 0x01, 0x10}, + {0xa1, 0x6e, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10}, + {0xd1, 0x6e, 0x29, 0xe6, 0x00, 0xbd, 0x03, 0x10}, + {0xd1, 0x6e, 0x2d, 0x41, 0x38, 0x68, 0x40, 0x10}, + {0xd1, 0x6e, 0x31, 0x2b, 0x00, 0x36, 0x00, 0x10}, + {0xd1, 0x6e, 0x35, 0x30, 0x30, 0x08, 0x00, 0x10}, + {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x33, 0x06, 0x10}, + {0xb1, 0x6e, 0x3d, 0x06, 0x02, 0x00, 0x00, 0x10}, + {} +}; +static const u8 po2030n_sensor_param1[][8] = { + {0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */ + {0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x16, 0x50, 0x40, 0x49, 0x40, 0x10}, +/*param2*/ + {0xa1, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x16, 0x52, 0x40, 0x48, 0x00, 0x10}, +/*after start*/ + {0xa1, 0x6e, 0x15, 0x0f, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */ + {0xa1, 0x6e, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */ + {0xa1, 0x6e, 0x1b, 0x53, 0x00, 0x00, 0x00, 0x10}, + {} +}; + +static const u8 soi768_sensor_init[][8] = { + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */ + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 soi768_sensor_param1[][8] = { + {0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10}, +/* */ +/* {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */ + {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */ + {0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10}, +/* the next sequence should be used for auto gain */ + {0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10}, + /* global gain ? : 07 - change with 0x15 at the end */ + {0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */ + {0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x2d, 0x00, 0x02, 0x00, 0x00, 0x10}, + /* exposure ? : 0200 - change with 0x1e at the end */ + {} +}; + static const u8 sp80708_sensor_init[][8] = { {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10}, @@ -1069,18 +1306,21 @@ static const u8 sp80708_sensor_param1[][8] = { {} }; -static const u8 (*sensor_init[11])[8] = { - adcm1700_sensor_init, /* ADCM1700 0 */ - hv7131r_sensor_init, /* HV7131R 1 */ - mi0360_sensor_init, /* MI0360 2 */ - mo4000_sensor_init, /* MO4000 3 */ - mt9v111_sensor_init, /* MT9V111 4 */ - om6802_sensor_init, /* OM6802 5 */ - ov7630_sensor_init, /* OV7630 6 */ - ov7648_sensor_init, /* OV7648 7 */ - ov7660_sensor_init, /* OV7660 8 */ - po1030_sensor_init, /* PO1030 9 */ - sp80708_sensor_init, /* SP80708 10 */ +static const u8 (*sensor_init[])[8] = { +[SENSOR_ADCM1700] = adcm1700_sensor_init, +[SENSOR_GC0307] = gc0307_sensor_init, +[SENSOR_HV7131R] = hv7131r_sensor_init, +[SENSOR_MI0360] = mi0360_sensor_init, +[SENSOR_MO4000] = mo4000_sensor_init, +[SENSOR_MT9V111] = mt9v111_sensor_init, +[SENSOR_OM6802] = om6802_sensor_init, +[SENSOR_OV7630] = ov7630_sensor_init, +[SENSOR_OV7648] = ov7648_sensor_init, +[SENSOR_OV7660] = ov7660_sensor_init, +[SENSOR_PO1030] = po1030_sensor_init, +[SENSOR_PO2030N] = po2030n_sensor_init, +[SENSOR_SOI768] = soi768_sensor_init, +[SENSOR_SP80708] = sp80708_sensor_init, }; /* read <len> bytes to gspca_dev->usb_buf */ @@ -1146,10 +1386,11 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) { struct sd *sd = (struct sd *) gspca_dev; - PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val); + PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val); switch (sd->sensor) { case SENSOR_ADCM1700: - case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */ + case SENSOR_OM6802: + case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */ gspca_dev->usb_buf[0] = 0x80 | (2 << 4); break; default: /* i2c command = a1 (400 kHz) */ @@ -1177,6 +1418,8 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) static void i2c_w8(struct gspca_dev *gspca_dev, const u8 *buffer) { + PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..", + buffer[2], buffer[3]); memcpy(gspca_dev->usb_buf, buffer, 8); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -1196,7 +1439,8 @@ static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len) switch (sd->sensor) { case SENSOR_ADCM1700: - case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */ + case SENSOR_OM6802: + case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */ mode[0] = 0x80 | 0x10; break; default: /* i2c command = 91 (400 kHz) */ @@ -1300,39 +1544,100 @@ static void mi0360_probe(struct gspca_dev *gspca_dev) } } -static void ov7648_probe(struct gspca_dev *gspca_dev) +static void ov7630_probe(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u16 val; /* check ov76xx */ reg_w1(gspca_dev, 0x17, 0x62); reg_w1(gspca_dev, 0x01, 0x08); sd->i2c_addr = 0x21; i2c_r(gspca_dev, 0x0a, 2); - if (gspca_dev->usb_buf[3] == 0x76) { /* ov76xx */ - PDEBUG(D_PROBE, "Sensor ov%02x%02x", - gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x7628) { /* soi768 */ + sd->sensor = SENSOR_SOI768; +/*fixme: only valid for 0c45:613e?*/ + gspca_dev->cam.input_flags = + V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP; + PDEBUG(D_PROBE, "Sensor soi768"); return; } + PDEBUG(D_PROBE, "Sensor ov%04x", val); +} - /* reset */ +static void ov7648_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 val; + + /* check ov76xx */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x0a, 2); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; reg_w1(gspca_dev, 0x01, 0x29); reg_w1(gspca_dev, 0x17, 0x42); + if ((val & 0xff00) == 0x7600) { /* ov76xx */ + PDEBUG(D_PROBE, "Sensor ov%04x", val); + return; + } /* check po1030 */ reg_w1(gspca_dev, 0x17, 0x62); reg_w1(gspca_dev, 0x01, 0x08); sd->i2c_addr = 0x6e; i2c_r(gspca_dev, 0x00, 2); - if (gspca_dev->usb_buf[3] == 0x10 /* po1030 */ - && gspca_dev->usb_buf[4] == 0x30) { + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x1030) { /* po1030 */ PDEBUG(D_PROBE, "Sensor po1030"); sd->sensor = SENSOR_PO1030; return; } - PDEBUG(D_PROBE, "Unknown sensor %02x%02x", - gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); + PDEBUG(D_PROBE, "Unknown sensor %04x", val); +} + +/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */ +static void po2030n_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 val; + + /* check gc0307 */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + reg_w1(gspca_dev, 0x02, 0x22); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x00, 1); + val = gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); /* reset */ + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x99) { /* gc0307 (?) */ + PDEBUG(D_PROBE, "Sensor gc0307"); + sd->sensor = SENSOR_GC0307; + return; + } + + /* check po2030n */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x0a); + sd->i2c_addr = 0x6e; + i2c_r(gspca_dev, 0x00, 2); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (val == 0x2030) { + PDEBUG(D_PROBE, "Sensor po2030n"); +/* sd->sensor = SENSOR_PO2030N; */ + } else { + PDEBUG(D_PROBE, "Unknown sensor ID %04x", val); + } } static void bridge_init(struct gspca_dev *gspca_dev, @@ -1355,8 +1660,11 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); switch (sd->sensor) { + case SENSOR_GC0307: case SENSOR_OV7660: case SENSOR_PO1030: + case SENSOR_PO2030N: + case SENSOR_SOI768: case SENSOR_SP80708: reg9a = reg9a_spec; break; @@ -1377,6 +1685,14 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x42); reg_w1(gspca_dev, 0x01, 0x42); break; + case SENSOR_GC0307: + msleep(50); + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x22); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + msleep(50); + break; case SENSOR_MT9V111: reg_w1(gspca_dev, 0x01, 0x61); reg_w1(gspca_dev, 0x17, 0x61); @@ -1414,11 +1730,18 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x42); break; case SENSOR_PO1030: + case SENSOR_SOI768: reg_w1(gspca_dev, 0x01, 0x61); reg_w1(gspca_dev, 0x17, 0x20); reg_w1(gspca_dev, 0x01, 0x60); reg_w1(gspca_dev, 0x01, 0x40); break; + case SENSOR_PO2030N: + reg_w1(gspca_dev, 0x01, 0x63); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x62); + reg_w1(gspca_dev, 0x01, 0x42); + break; case SENSOR_OV7660: /* fall thru */ case SENSOR_SP80708: @@ -1523,9 +1846,15 @@ static int sd_init(struct gspca_dev *gspca_dev) case SENSOR_MI0360: mi0360_probe(gspca_dev); break; + case SENSOR_OV7630: + ov7630_probe(gspca_dev); + break; case SENSOR_OV7648: ov7648_probe(gspca_dev); break; + case SENSOR_PO2030N: + po2030n_probe(gspca_dev); + break; } regGpio[1] = 0x70; reg_w(gspca_dev, 0x01, regGpio, 2); @@ -1558,6 +1887,18 @@ static u32 setexposure(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; switch (sd->sensor) { + case SENSOR_GC0307: { + int a, b; + + /* expo = 0..255 -> a = 19..43 */ + a = 19 + expo * 25 / 256; + i2c_w1(gspca_dev, 0x68, a); + a -= 12; + b = a * a * 4; /* heuristic */ + i2c_w1(gspca_dev, 0x03, b >> 8); + i2c_w1(gspca_dev, 0x04, b); + break; + } case SENSOR_HV7131R: { u8 Expodoit[] = { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 }; @@ -1668,6 +2009,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) expo = sd->brightness >> 4; sd->exposure = setexposure(gspca_dev, expo); break; + case SENSOR_GC0307: case SENSOR_MT9V111: expo = sd->brightness >> 8; sd->exposure = setexposure(gspca_dev, expo); @@ -1703,7 +2045,7 @@ static void setcolors(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int i, v; u8 reg8a[12]; /* U & V gains */ - static s16 uv[6] = { /* same as reg84 in signed decimal */ + static const s16 uv[6] = { /* same as reg84 in signed decimal */ -24, -38, 64, /* UR UG UB */ 62, -51, -9 /* VR VG VB */ }; @@ -1744,9 +2086,12 @@ static void setgamma(struct gspca_dev *gspca_dev) case SENSOR_MT9V111: gamma_base = gamma_spec_1; break; - case SENSOR_SP80708: + case SENSOR_GC0307: gamma_base = gamma_spec_2; break; + case SENSOR_SP80708: + gamma_base = gamma_spec_3; + break; default: gamma_base = gamma_def; break; @@ -1937,14 +2282,17 @@ static int sd_start(struct gspca_dev *gspca_dev) static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; static const u8 CA_adcm1700[] = { 0x14, 0xec, 0x0a, 0xf6 }; + static const u8 CA_po2030n[] = + { 0x1e, 0xe2, 0x14, 0xec }; static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ + static const u8 CE_gc0307[] = + { 0x32, 0xce, 0x2d, 0xd3 }; static const u8 CE_ov76xx[] = { 0x32, 0xdd, 0x32, 0xdd }; + static const u8 CE_po2030n[] = + { 0x14, 0xe7, 0x1e, 0xdd }; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -1996,6 +2344,9 @@ static int sd_start(struct gspca_dev *gspca_dev) } reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); switch (sd->sensor) { + case SENSOR_GC0307: + reg17 = 0xa2; + break; case SENSOR_MT9V111: reg17 = 0xe0; break; @@ -2007,9 +2358,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0x20; break; case SENSOR_OV7660: + case SENSOR_SOI768: reg17 = 0xa0; break; case SENSOR_PO1030: + case SENSOR_PO2030N: reg17 = 0xa0; break; default: @@ -2034,12 +2387,18 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_SP80708: reg_w1(gspca_dev, 0x9a, 0x05); break; + case SENSOR_GC0307: case SENSOR_MT9V111: reg_w1(gspca_dev, 0x9a, 0x07); break; + case SENSOR_OV7630: case SENSOR_OV7648: reg_w1(gspca_dev, 0x9a, 0x0a); break; + case SENSOR_PO2030N: + case SENSOR_SOI768: + reg_w1(gspca_dev, 0x9a, 0x06); + break; default: reg_w1(gspca_dev, 0x9a, 0x08); break; @@ -2064,6 +2423,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg1 = 0x46; reg17 = 0xe2; break; + case SENSOR_GC0307: + init = gc0307_sensor_param1; + reg17 = 0xa2; + reg1 = 0x44; + break; case SENSOR_MO4000: if (mode) { /* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ @@ -2087,6 +2451,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0x64; /* 640 MCKSIZE */ break; case SENSOR_OV7630: + init = ov7630_sensor_param1; reg17 = 0xe2; reg1 = 0x44; break; @@ -2113,6 +2478,16 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0xa2; reg1 = 0x44; break; + case SENSOR_PO2030N: + init = po2030n_sensor_param1; + reg1 = 0x46; + reg17 = 0xa2; + break; + case SENSOR_SOI768: + init = soi768_sensor_param1; + reg1 = 0x44; + reg17 = 0xa2; + break; default: /* case SENSOR_SP80708: */ init = sp80708_sensor_param1; @@ -2132,17 +2507,33 @@ static int sd_start(struct gspca_dev *gspca_dev) } reg_w(gspca_dev, 0xc0, C0, 6); - if (sd->sensor == SENSOR_ADCM1700) + switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_GC0307: + case SENSOR_SOI768: reg_w(gspca_dev, 0xca, CA_adcm1700, 4); - else + break; + case SENSOR_PO2030N: + reg_w(gspca_dev, 0xca, CA_po2030n, 4); + break; + default: reg_w(gspca_dev, 0xca, CA, 4); + break; + } switch (sd->sensor) { case SENSOR_ADCM1700: case SENSOR_OV7630: case SENSOR_OV7648: case SENSOR_OV7660: + case SENSOR_SOI768: reg_w(gspca_dev, 0xce, CE_ov76xx, 4); break; + case SENSOR_GC0307: + reg_w(gspca_dev, 0xce, CE_gc0307, 4); + break; + case SENSOR_PO2030N: + reg_w(gspca_dev, 0xce, CE_po2030n, 4); + break; default: reg_w(gspca_dev, 0xce, CE, 4); /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */ @@ -2161,6 +2552,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setvflip(sd); setbrightness(gspca_dev); setcontrast(gspca_dev); + setcolors(gspca_dev); setautogain(gspca_dev); setfreq(gspca_dev); return 0; @@ -2175,11 +2567,16 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; static const u8 stopov7648[] = { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 }; + static const u8 stopsoi768[] = + { 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 }; u8 data; const u8 *sn9c1xx; data = 0x0b; switch (sd->sensor) { + case SENSOR_GC0307: + data = 0x29; + break; case SENSOR_HV7131R: i2c_w8(gspca_dev, stophv7131); data = 0x2b; @@ -2196,6 +2593,10 @@ static void sd_stopN(struct gspca_dev *gspca_dev) case SENSOR_PO1030: data = 0x29; break; + case SENSOR_SOI768: + i2c_w8(gspca_dev, stopsoi768); + data = 0x29; + break; } sn9c1xx = sn_tb[sd->sensor]; reg_w1(gspca_dev, 0x01, sn9c1xx[1]); @@ -2206,13 +2607,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* reg_w1(gspca_dev, 0xf1, 0x01); */ } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2233,6 +2627,14 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (delta < luma_mean - luma_delta || delta > luma_mean + luma_delta) { switch (sd->sensor) { + case SENSOR_GC0307: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 6; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) expotimes); + break; case SENSOR_HV7131R: expotimes = sd->exposure >> 8; expotimes += (luma_mean - delta) >> 4; @@ -2584,7 +2986,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, .get_jcomp = sd_get_jcomp, @@ -2656,7 +3057,7 @@ static const __devinitdata struct usb_device_id device_table[] = { #endif {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)}, {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)}, -/* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x614a), BS(SN9C120, ADCM1700)}, /*sn9c120b*/ diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index b9c80e2103b9..7bb2355005dc 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -22,6 +22,7 @@ #define MODULE_NAME "spca561" +#include <linux/input.h> #include "gspca.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); @@ -249,9 +250,9 @@ static const __u16 Pb100_2map8300[][2] = { }; static const __u16 spca561_161rev12A_data1[][2] = { - {0x29, 0x8118}, /* white balance - was 21 */ - {0x08, 0x8114}, /* white balance - was 01 */ - {0x0e, 0x8112}, /* white balance - was 00 */ + {0x29, 0x8118}, /* Control register (various enable bits) */ + {0x08, 0x8114}, /* GPIO: Led off */ + {0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */ {0x00, 0x8102}, /* white balance - new */ {0x92, 0x8804}, {0x04, 0x8802}, /* windows uses 08 */ @@ -263,15 +264,11 @@ static const __u16 spca561_161rev12A_data2[][2] = { {0x07, 0x8601}, {0x07, 0x8602}, {0x04, 0x8501}, - {0x21, 0x8118}, {0x07, 0x8201}, /* windows uses 02 */ {0x08, 0x8200}, {0x01, 0x8200}, - {0x00, 0x8114}, - {0x01, 0x8114}, /* windows uses 00 */ - {0x90, 0x8604}, {0x00, 0x8605}, {0xb0, 0x8603}, @@ -302,6 +299,9 @@ static const __u16 spca561_161rev12A_data2[][2] = { {0xf0, 0x8505}, {0x32, 0x850a}, /* {0x99, 0x8700}, * - white balance - new (removed) */ + /* HDG we used to do this in stop0, making the init state and the state + after a start / stop different, so do this here instead. */ + {0x29, 0x8118}, {} }; @@ -645,6 +645,9 @@ static int sd_start_12a(struct gspca_dev *gspca_dev) setwhite(gspca_dev); setgain(gspca_dev); setexposure(gspca_dev); + + /* Led ON (bit 3 -> 0 */ + reg_w_val(gspca_dev->dev, 0x8114, 0x00); return 0; } static int sd_start_72a(struct gspca_dev *gspca_dev) @@ -691,26 +694,14 @@ static void sd_stopN(struct gspca_dev *gspca_dev) if (sd->chip_revision == Rev012A) { reg_w_val(gspca_dev->dev, 0x8112, 0x0e); + /* Led Off (bit 3 -> 1 */ + reg_w_val(gspca_dev->dev, 0x8114, 0x08); } else { reg_w_val(gspca_dev->dev, 0x8112, 0x20); /* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */ } } -/* called on streamoff with alt 0 and on disconnect */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - if (!gspca_dev->present) - return; - if (sd->chip_revision == Rev012A) { - reg_w_val(gspca_dev->dev, 0x8118, 0x29); - reg_w_val(gspca_dev->dev, 0x8114, 0x08); - } -/* reg_w_val(gspca_dev->dev, 0x8114, 0); */ -} - static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -788,6 +779,23 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, switch (*data++) { /* sequence number */ case 0: /* start of frame */ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + /* This should never happen */ + if (len < 2) { + PDEBUG(D_ERR, "Short SOF packet, ignoring"); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + +#ifdef CONFIG_INPUT + if (data[0] & 0x20) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + } +#endif + if (data[1] & 0x10) { /* compressed bayer */ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); @@ -1028,8 +1036,10 @@ static const struct sd_desc sd_desc_12a = { .init = sd_init_12a, .start = sd_start_12a, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, +#ifdef CONFIG_INPUT + .other_input = 1, +#endif }; static const struct sd_desc sd_desc_72a = { .name = MODULE_NAME, @@ -1039,9 +1049,11 @@ static const struct sd_desc sd_desc_72a = { .init = sd_init_72a, .start = sd_start_72a, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, +#ifdef CONFIG_INPUT + .other_input = 1, +#endif }; static const struct sd_desc *sd_desc[2] = { &sd_desc_12a, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 668a7536af90..63014372adbc 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -44,7 +44,10 @@ struct sd { u8 gamma; u8 sharpness; u8 freq; - u8 whitebalance; + u8 red_balance; /* split balance */ + u8 blue_balance; + u8 global_gain; /* aka gain */ + u8 whitebalance; /* set default r/g/b and activate */ u8 mirror; u8 effect; @@ -70,8 +73,17 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); + + static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setglobal_gain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getglobal_gain(struct gspca_dev *gspca_dev, __s32 *val); + static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val); @@ -79,6 +91,7 @@ static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val); static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu); + static const struct ctrl sd_ctrls[] = { { { @@ -139,7 +152,7 @@ static const struct ctrl sd_ctrls[] = { }, { { - .id = V4L2_CID_GAIN, /* here, i activate only the lowlight, + .id = V4L2_CID_BACKLIGHT_COMPENSATION, /* Activa lowlight, * some apps dont bring up the * backligth_compensation control) */ .type = V4L2_CTRL_TYPE_INTEGER, @@ -183,7 +196,7 @@ static const struct ctrl sd_ctrls[] = { { { - .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .id = V4L2_CID_AUTO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "White Balance", .minimum = 0, @@ -223,6 +236,48 @@ static const struct ctrl sd_ctrls[] = { .set = sd_seteffect, .get = sd_geteffect }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define BLUE_BALANCE_DEF 0x20 + .default_value = BLUE_BALANCE_DEF, + }, + .set = sd_setblue_balance, + .get = sd_getblue_balance, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define RED_BALANCE_DEF 0x20 + .default_value = RED_BALANCE_DEF, + }, + .set = sd_setred_balance, + .get = sd_getred_balance, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0x10, + .maximum = 0x40, + .step = 1, +#define global_gain_DEF 0x20 + .default_value = global_gain_DEF, + }, + .set = sd_setglobal_gain, + .get = sd_getglobal_gain, + }, }; static char *effects_control[] = { @@ -523,6 +578,10 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, u8 *tmpbuf; tmpbuf = kmalloc(len, GFP_KERNEL); + if (!tmpbuf) { + err("Out of memory"); + return; + } memcpy(tmpbuf, buffer, len); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -542,10 +601,15 @@ static void reg_w_ixbuf(struct gspca_dev *gspca_dev, int i; u8 *p, *tmpbuf; - if (len * 2 <= USB_BUF_SZ) + if (len * 2 <= USB_BUF_SZ) { p = tmpbuf = gspca_dev->usb_buf; - else + } else { p = tmpbuf = kmalloc(len * 2, GFP_KERNEL); + if (!tmpbuf) { + err("Out of memory"); + return; + } + } i = len; while (--i >= 0) { *p++ = reg++; @@ -642,6 +706,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->whitebalance = WHITE_BALANCE_DEF; sd->sharpness = SHARPNESS_DEF; sd->effect = EFFECTS_DEF; + sd->red_balance = RED_BALANCE_DEF; + sd->blue_balance = BLUE_BALANCE_DEF; + sd->global_gain = global_gain_DEF; + return 0; } @@ -693,18 +761,40 @@ static void setgamma(struct gspca_dev *gspca_dev) reg_w_ixbuf(gspca_dev, 0x90, gamma_table[sd->gamma], sizeof gamma_table[0]); } +static void setglobalgain(struct gspca_dev *gspca_dev) +{ -static void setwhitebalance(struct gspca_dev *gspca_dev) + struct sd *sd = (struct sd *) gspca_dev; + reg_w(gspca_dev, (sd->red_balance << 8) + 0x87); + reg_w(gspca_dev, (sd->blue_balance << 8) + 0x88); + reg_w(gspca_dev, (sd->global_gain << 8) + 0x89); +} + +/* Generic fnc for r/b balance, exposure and whitebalance */ +static void setbalance(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 white_balance[8] = - {0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38}; + /* on whitebalance leave defaults values */ + if (sd->whitebalance) { + reg_w(gspca_dev, 0x3c80); + } else { + reg_w(gspca_dev, 0x3880); + /* shoud we wait here.. */ + /* update and reset 'global gain' with webcam parameters */ + sd->red_balance = reg_r(gspca_dev, 0x0087); + sd->blue_balance = reg_r(gspca_dev, 0x0088); + sd->global_gain = reg_r(gspca_dev, 0x0089); + setglobalgain(gspca_dev); + } + +} + - if (sd->whitebalance) - white_balance[7] = 0x3c; - reg_w_buf(gspca_dev, white_balance, sizeof white_balance); +static void setwhitebalance(struct gspca_dev *gspca_dev) +{ + setbalance(gspca_dev); } static void setsharpness(struct gspca_dev *gspca_dev) @@ -1018,6 +1108,66 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } + +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue_balance = val; + if (gspca_dev->streaming) + reg_w(gspca_dev, (val << 8) + 0x88); + return 0; +} + +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue_balance; + return 0; +} + +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red_balance = val; + if (gspca_dev->streaming) + reg_w(gspca_dev, (val << 8) + 0x87); + + return 0; +} + +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red_balance; + return 0; +} + + + +static int sd_setglobal_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->global_gain = val; + if (gspca_dev->streaming) + setglobalgain(gspca_dev); + + return 0; +} + +static int sd_getglobal_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->global_gain; + return 0; +} + + static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 4989f9afb46e..732c3dfe46ff 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -1,9 +1,9 @@ /* - * Z-star vc0321 library - * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl - * Copyright (C) 2006 Michel Xhaard + * Z-star vc0321 library * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr> + * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl + * Copyright (C) 2006 Michel Xhaard * * 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 @@ -24,7 +24,7 @@ #include "gspca.h" -MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -1971,268 +1971,489 @@ static const u8 ov7660_NoFliker[][4] = { {} }; -static const u8 ov7670_initVGA_JPG[][4] = { +static const u8 ov7670_InitVGA[][4] = { {0xb3, 0x01, 0x05, 0xcc}, - {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, - {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x00, 0x66, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb0, 0x16, 0x01, 0xcc}, {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0x41, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, - {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, - {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, - {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, - {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, - {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, - {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, - {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, - {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, - {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, - {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, - {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, - {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, - {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, - {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, - {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, - {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, - {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, - {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, - {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, - {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xbc, 0x00, 0x41, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, + {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, + {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, + {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, + {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, + {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, + {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, + {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, + {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, + {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, + {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, + {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, + {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, + {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, + {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, + {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, + {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, + {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, + {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ {0x00, 0x21, 0x02, 0xaa}, - {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, - {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, - {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, - {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, - {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, - {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, - {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, - {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, - {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, - {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, - {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, - {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, - {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, - {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, - {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, - {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, - {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, - {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, - {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, - {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, - {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, - {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, - {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, - {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, - {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, - {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, - {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, - {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, - {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, - {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, - {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, - {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, - {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, - {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, - {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, - {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, - {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, - {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, - {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, - {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, - {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, - {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, - {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, - {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, - {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, - {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, - {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, - {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, - {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, - {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, - {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, - {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, - {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, + {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, + {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, + {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, + {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, + {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, + {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, + {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, + {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, + {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, + {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, + {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, + {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, + {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, + {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, + {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, + {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, + {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, + {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, + {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, + {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, + {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, + {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, + {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, + {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, + {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, + {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, + {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, + {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, + {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, + {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, + {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, + {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, + {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, + {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, + {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, + {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, + {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, + {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, + {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, + {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, + {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, + {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, + {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, + {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, + {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, + {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, + {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, - {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, - {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, - {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, - {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, - {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x13, 0xcc}, - {0xb6, 0x18, 0x02, 0xcc}, {0xb6, 0x17, 0x58, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x24, 0x80, 0xaa}, + {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, + {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, + {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, + {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, {0x00, 0x77, 0x05, 0xaa}, {}, }; -static const u8 ov7670_initQVGA_JPG[][4] = { - {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, - {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, - {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, - {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, - {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, - {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, - {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, - {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, - {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, - {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, - {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, - {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, - {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, - {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, - {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, - {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, - {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, - {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, - {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, - {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, - {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, - {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, - {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, - {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, - {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, +static const u8 ov7670_InitQVGA[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb0, 0x16, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, + {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, + {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, + {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, + {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, + {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, + {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, + {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, + {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, + {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, + {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, + {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, + {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, + {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, + {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, + {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, + {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, + {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, + {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ {0x00, 0x21, 0x02, 0xaa}, - {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, - {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, - {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, - {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, - {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, - {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, - {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, - {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, - {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, - {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, - {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, - {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, - {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, - {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, - {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, - {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, - {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, - {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, - {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, - {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, - {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, - {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, - {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, - {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, - {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, - {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, - {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, - {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, - {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, - {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, - {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, - {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, - {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, - {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, - {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, - {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, - {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, - {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, - {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, - {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, - {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, - {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, - {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, - {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, - {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, - {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, - {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, - {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, - {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, - {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, - {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, - {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, - {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, - {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, - {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, - {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, + {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, + {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, + {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, + {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, + {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, + {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, + {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, + {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, + {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, + {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, + {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, + {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, + {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, + {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, + {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, + {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, + {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, + {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, + {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, + {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, + {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, + {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, + {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, + {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, + {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, + {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, + {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, + {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, + {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, + {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, + {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, + {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, + {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, + {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, + {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, + {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, + {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, + {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, + {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, + {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, + {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, + {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, + {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, + {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, + {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, + {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, + {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, + {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb1, 0x0c, 0xaa}, {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ {0x00, 0xaa, 0x14, 0xaa}, - {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, - {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, - {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, - {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, - {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, - {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, - {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, - {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, - {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, - {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, - {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x21, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, - {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, - {0x00, 0x77, 0x05, 0xaa }, + {0x00, 0x24, 0x80, 0xaa}, + {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, + {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, + {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, + {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, + {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, + {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, + {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, + {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, + {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa}, {}, }; @@ -3117,6 +3338,10 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = bi_mode; cam->nmodes = ARRAY_SIZE(bi_mode); break; + case SENSOR_OV7670: + cam->cam_mode = bi_mode; + cam->nmodes = ARRAY_SIZE(bi_mode) - 1; + break; default: cam->cam_mode = vc0323_mode; cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; @@ -3329,14 +3554,6 @@ static int sd_start(struct gspca_dev *gspca_dev) else init = ov7660_initVGA_data; /* 640x480 */ break; - case SENSOR_OV7670: - /*GammaT = ov7660_gamma; */ - /*MatrixT = ov7660_matrix; */ - if (mode) - init = ov7670_initQVGA_JPG; /* 320x240 */ - else - init = ov7670_initVGA_JPG; /* 640x480 */ - break; case SENSOR_MI0360: GammaT = mi1320_gamma; MatrixT = mi0360_matrix; @@ -3373,6 +3590,9 @@ static int sd_start(struct gspca_dev *gspca_dev) MatrixT = mi1320_matrix; init = mi1320_soc_init[mode]; break; + case SENSOR_OV7670: + init = mode == 1 ? ov7670_InitVGA : ov7670_InitQVGA; + break; case SENSOR_PO3130NC: GammaT = po3130_gamma; MatrixT = po3130_matrix; @@ -3426,7 +3646,13 @@ static int sd_start(struct gspca_dev *gspca_dev) sethvflip(gspca_dev); setlightfreq(gspca_dev); } - if (sd->sensor == SENSOR_POxxxx) { + switch (sd->sensor) { + case SENSOR_OV7670: + reg_w(gspca_dev->dev, 0x87, 0xffff, 0xffff); + reg_w(gspca_dev->dev, 0x88, 0xff00, 0xf0f1); + reg_w(gspca_dev->dev, 0xa0, 0x0000, 0xbfff); + break; + case SENSOR_POxxxx: setcolors(gspca_dev); setbrightness(gspca_dev); setcontrast(gspca_dev); @@ -3435,6 +3661,7 @@ static int sd_start(struct gspca_dev *gspca_dev) msleep(80); reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); usb_exchange(gspca_dev, poxxxx_init_end_2); + break; } return 0; } diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 7d7814c43f92..d02aa5c8472a 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -40,7 +40,6 @@ static int force_sensor = -1; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - u8 brightness; u8 contrast; u8 gamma; u8 autogain; @@ -80,8 +79,6 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); @@ -94,21 +91,6 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { -#define BRIGHTNESS_IDX 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, -#define BRIGHTNESS_DEF 128 - .default_value = BRIGHTNESS_DEF, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, { { .id = V4L2_CID_CONTRAST, @@ -150,7 +132,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define LIGHTFREQ_IDX 4 +#define LIGHTFREQ_IDX 3 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -6004,33 +5986,6 @@ static void setmatrix(struct gspca_dev *gspca_dev) reg_w(gspca_dev->dev, matrix[i], 0x010a + i); } -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 brightness; - - switch (sd->sensor) { - case SENSOR_GC0305: - case SENSOR_OV7620: - case SENSOR_PAS202B: - case SENSOR_PO2030: - return; - } -/*fixme: is it really write to 011d and 018d for all other sensors? */ - brightness = sd->brightness; - reg_w(gspca_dev->dev, brightness, 0x011d); - switch (sd->sensor) { - case SENSOR_ADCM2700: - case SENSOR_HV7131B: - return; - } - if (brightness < 0x70) - brightness += 0x10; - else - brightness = 0x80; - reg_w(gspca_dev->dev, brightness, 0x018d); -} - static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -6059,8 +6014,8 @@ static void setcontrast(struct gspca_dev *gspca_dev) int g, i, k, adj, gp; u8 gr[16]; static const u8 delta_tb[16] = /* delta for contrast */ - {0x15, 0x0d, 0x0a, 0x09, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06, + 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02}; static const u8 gamma_tb[6][16] = { {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f, 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff}, @@ -6082,11 +6037,11 @@ static void setcontrast(struct gspca_dev *gspca_dev) adj = 0; gp = 0; for (i = 0; i < 16; i++) { - g = Tgamma[i] - delta_tb[i] * k / 128 - adj / 2; + g = Tgamma[i] - delta_tb[i] * k / 256 - adj / 2; if (g > 0xff) g = 0xff; - else if (g <= 0) - g = 1; + else if (g < 0) + g = 0; reg_w(dev, g, 0x0120 + i); /* gamma */ if (k > 0) adj--; @@ -6203,13 +6158,13 @@ static int setlightfreq(struct gspca_dev *gspca_dev) pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, /* SENSOR_PAS202B 13 */ - {pas202b_NoFlikerScale, pas202b_NoFliker, - pas202b_50HZScale, pas202b_50HZ, - pas202b_60HZScale, pas202b_60HZ}, + {pas202b_NoFliker, pas202b_NoFlikerScale, + pas202b_50HZ, pas202b_50HZScale, + pas202b_60HZ, pas202b_60HZScale}, /* SENSOR_PB0330 14 */ - {pb0330_NoFlikerScale, pb0330_NoFliker, - pb0330_50HZScale, pb0330_50HZ, - pb0330_60HZScale, pb0330_60HZ}, + {pb0330_NoFliker, pb0330_NoFlikerScale, + pb0330_50HZ, pb0330_50HZScale, + pb0330_60HZ, pb0330_60HZScale}, /* SENSOR_PO2030 15 */ {po2030_NoFliker, po2030_NoFliker, po2030_50HZ, po2030_50HZ, @@ -6789,7 +6744,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(broken_vga_mode); break; } - sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->gamma = gamma[sd->sensor]; sd->autogain = AUTOGAIN_DEF; @@ -6797,12 +6751,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->quality = QUALITY_DEF; switch (sd->sensor) { - case SENSOR_GC0305: - case SENSOR_OV7620: - case SENSOR_PAS202B: - case SENSOR_PO2030: - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); - break; case SENSOR_HV7131B: case SENSOR_HV7131C: case SENSOR_OV7630C: @@ -6893,7 +6841,6 @@ static int sd_start(struct gspca_dev *gspca_dev) } setmatrix(gspca_dev); - setbrightness(gspca_dev); switch (sd->sensor) { case SENSOR_ADCM2700: case SENSOR_OV7620: @@ -7015,24 +6962,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightness(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; -} - static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 2fc9865fd486..830d47b05e1d 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -373,9 +373,6 @@ static int hdpvr_probe(struct usb_interface *interface, } #endif /* CONFIG_I2C */ - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, dev); - /* let the user know what node this device is now attached to */ v4l2_info(&dev->v4l2_dev, "device now attached to %s\n", video_device_node_name(dev->video_dev)); @@ -391,44 +388,24 @@ error: static void hdpvr_disconnect(struct usb_interface *interface) { - struct hdpvr_device *dev; - - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); + struct hdpvr_device *dev = to_hdpvr_dev(usb_get_intfdata(interface)); + v4l2_info(&dev->v4l2_dev, "device %s disconnected\n", + video_device_node_name(dev->video_dev)); /* prevent more I/O from starting and stop any ongoing */ mutex_lock(&dev->io_mutex); dev->status = STATUS_DISCONNECTED; - v4l2_device_disconnect(&dev->v4l2_dev); - video_unregister_device(dev->video_dev); wake_up_interruptible(&dev->wait_data); wake_up_interruptible(&dev->wait_buffer); mutex_unlock(&dev->io_mutex); + v4l2_device_disconnect(&dev->v4l2_dev); msleep(100); flush_workqueue(dev->workqueue); mutex_lock(&dev->io_mutex); hdpvr_cancel_queue(dev); - destroy_workqueue(dev->workqueue); mutex_unlock(&dev->io_mutex); - - /* deregister I2C adapter */ -#ifdef CONFIG_I2C - mutex_lock(&dev->i2c_mutex); - if (dev->i2c_adapter) - i2c_del_adapter(dev->i2c_adapter); - kfree(dev->i2c_adapter); - dev->i2c_adapter = NULL; - mutex_unlock(&dev->i2c_mutex); -#endif /* CONFIG_I2C */ - + video_unregister_device(dev->video_dev); atomic_dec(&dev_nr); - - v4l2_info(&dev->v4l2_dev, "device %s disconnected\n", - video_device_node_name(dev->video_dev)); - - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev->usbc_buf); - kfree(dev); } diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 196f82de48f0..d2f0ee29737f 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -1214,6 +1214,24 @@ static void hdpvr_device_release(struct video_device *vdev) struct hdpvr_device *dev = video_get_drvdata(vdev); hdpvr_delete(dev); + mutex_lock(&dev->io_mutex); + destroy_workqueue(dev->workqueue); + mutex_unlock(&dev->io_mutex); + + v4l2_device_unregister(&dev->v4l2_dev); + + /* deregister I2C adapter */ +#ifdef CONFIG_I2C + mutex_lock(&dev->i2c_mutex); + if (dev->i2c_adapter) + i2c_del_adapter(dev->i2c_adapter); + kfree(dev->i2c_adapter); + dev->i2c_adapter = NULL; + mutex_unlock(&dev->i2c_mutex); +#endif /* CONFIG_I2C */ + + kfree(dev->usbc_buf); + kfree(dev); } static const struct video_device hdpvr_video_template = { diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h index 49ae25d83d10..b0f046df3cd8 100644 --- a/drivers/media/video/hdpvr/hdpvr.h +++ b/drivers/media/video/hdpvr/hdpvr.h @@ -111,6 +111,11 @@ struct hdpvr_device { u8 *usbc_buf; }; +static inline struct hdpvr_device *to_hdpvr_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct hdpvr_device, v4l2_dev); +} + /* buffer one bulk urb of data */ struct hdpvr_buffer { diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index da18d698e7f2..29d439742653 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -61,9 +61,9 @@ module_param(hauppauge, int, 0644); /* Choose Hauppauge remote */ MODULE_PARM_DESC(hauppauge, "Specify Hauppauge remote: 0=black, 1=grey (defaults to 0)"); -#define DEVNAME "ir-kbd-i2c" +#define MODULE_NAME "ir-kbd-i2c" #define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG DEVNAME ": " fmt , ## arg) + printk(KERN_DEBUG MODULE_NAME ": " fmt , ## arg) /* ----------------------------------------------------------------------- */ @@ -297,7 +297,7 @@ static void ir_work(struct work_struct *work) static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; const char *name = NULL; u64 ir_type = 0; struct IR_i2c *ir; @@ -322,13 +322,13 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "Pixelview"; ir->get_key = get_key_pixelview; ir_type = IR_TYPE_OTHER; - ir_codes = &ir_codes_empty_table; + ir_codes = RC_MAP_EMPTY; break; case 0x4b: name = "PV951"; ir->get_key = get_key_pv951; ir_type = IR_TYPE_OTHER; - ir_codes = &ir_codes_pv951_table; + ir_codes = RC_MAP_PV951; break; case 0x18: case 0x1f: @@ -337,22 +337,22 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->get_key = get_key_haup; ir_type = IR_TYPE_RC5; if (hauppauge == 1) { - ir_codes = &ir_codes_hauppauge_new_table; + ir_codes = RC_MAP_HAUPPAUGE_NEW; } else { - ir_codes = &ir_codes_rc5_tv_table; + ir_codes = RC_MAP_RC5_TV; } break; case 0x30: name = "KNC One"; ir->get_key = get_key_knc1; ir_type = IR_TYPE_OTHER; - ir_codes = &ir_codes_empty_table; + ir_codes = RC_MAP_EMPTY; break; case 0x6b: name = "FusionHDTV"; ir->get_key = get_key_fusionhdtv; ir_type = IR_TYPE_RC5; - ir_codes = &ir_codes_fusionhdtv_mce_table; + ir_codes = RC_MAP_FUSIONHDTV_MCE; break; case 0x0b: case 0x47: @@ -365,9 +365,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_type = IR_TYPE_RC5; ir->get_key = get_key_haup_xvr; if (hauppauge == 1) { - ir_codes = &ir_codes_hauppauge_new_table; + ir_codes = RC_MAP_HAUPPAUGE_NEW; } else { - ir_codes = &ir_codes_rc5_tv_table; + ir_codes = RC_MAP_RC5_TV; } } else { /* Handled by saa7134-input */ @@ -379,7 +379,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) name = "AVerMedia Cardbus remote"; ir->get_key = get_key_avermedia_cardbus; ir_type = IR_TYPE_OTHER; - ir_codes = &ir_codes_avermedia_cardbus_table; + ir_codes = RC_MAP_AVERMEDIA_CARDBUS; break; } @@ -447,11 +447,11 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) input_dev->name = ir->name; input_dev->phys = ir->phys; - err = ir_input_register(ir->input, ir->ir_codes, NULL); + err = ir_input_register(ir->input, ir->ir_codes, NULL, MODULE_NAME); if (err) goto err_out_free; - printk(DEVNAME ": %s detected at %s [%s]\n", + printk(MODULE_NAME ": %s detected at %s [%s]\n", ir->input->name, ir->input->phys, adap->name); /* start polling via eventd */ diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 9a250548be4d..1b79475ca134 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -1293,7 +1293,6 @@ int ivtv_init_on_first_open(struct ivtv *itv) ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); ivtv_init_mpeg_decoder(itv); } - ivtv_s_std(NULL, &fh, &itv->tuner_std); /* On a cx23416 this seems to be able to enable DMA to the chip? */ if (!itv->has_cx23415) @@ -1310,6 +1309,10 @@ int ivtv_init_on_first_open(struct ivtv *itv) } else ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); + + /* For cards with video out, this call needs interrupts enabled */ + ivtv_s_std(NULL, &fh, &itv->tuner_std); + return 0; } diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 5028e31c564a..5b45fd2b2645 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -63,6 +63,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> +#include <media/v4l2-fh.h> #include <media/tuner.h> #include <media/cx2341x.h> #include <media/ir-kbd-i2c.h> @@ -116,6 +117,9 @@ #define IVTV_REG_VPU (0x9058) #define IVTV_REG_APU (0xA064) +/* Other registers */ +#define IVTV_REG_DEC_LINE_FIELD (0x28C0) + /* debugging */ extern int ivtv_debug; @@ -372,6 +376,7 @@ struct ivtv_stream { }; struct ivtv_open_id { + struct v4l2_fh fh; u32 open_id; /* unique ID for this file descriptor */ int type; /* stream type */ int yuv_frames; /* 1: started OUT_UDMA_YUV output mode */ @@ -379,6 +384,11 @@ struct ivtv_open_id { struct ivtv *itv; }; +static inline struct ivtv_open_id *fh2id(struct v4l2_fh *fh) +{ + return container_of(fh, struct ivtv_open_id, fh); +} + struct yuv_frame_info { u32 update; diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index babcabd73c08..abf410943cc9 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -32,6 +32,7 @@ #include "ivtv-yuv.h" #include "ivtv-ioctl.h" #include "ivtv-cards.h" +#include <media/v4l2-event.h> #include <media/saa7115.h> /* This function tries to claim the stream for a specific file descriptor. @@ -506,7 +507,7 @@ int ivtv_start_capture(struct ivtv_open_id *id) ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos) { - struct ivtv_open_id *id = filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; int rc; @@ -541,7 +542,7 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed) ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) { - struct ivtv_open_id *id = filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; struct yuv_playback_info *yi = &itv->yuv_info; @@ -711,19 +712,31 @@ retry: unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) { - struct ivtv_open_id *id = filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; int res = 0; /* add stream's waitq to the poll list */ IVTV_DEBUG_HI_FILE("Decoder poll\n"); - poll_wait(filp, &s->waitq, wait); - set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); - if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) || - test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) - res = POLLPRI; + /* If there are subscribed events, then only use the new event + API instead of the old video.h based API. */ + if (!list_empty(&id->fh.events->subscribed)) { + poll_wait(filp, &id->fh.events->wait, wait); + /* Turn off the old-style vsync events */ + clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + if (v4l2_event_pending(&id->fh)) + res = POLLPRI; + } else { + /* This is the old-style API which is here only for backwards + compatibility. */ + poll_wait(filp, &s->waitq, wait); + set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) || + test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) + res = POLLPRI; + } /* Allow write if buffers are available for writing */ if (s->q_free.buffers) @@ -733,7 +746,7 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) { - struct ivtv_open_id *id = filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags); @@ -833,13 +846,16 @@ static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts) int ivtv_v4l2_close(struct file *filp) { - struct ivtv_open_id *id = filp->private_data; + struct v4l2_fh *fh = filp->private_data; + struct ivtv_open_id *id = fh2id(fh); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; IVTV_DEBUG_FILE("close %s\n", s->name); - v4l2_prio_close(&itv->prio, &id->prio); + v4l2_prio_close(&itv->prio, id->prio); + v4l2_fh_del(fh); + v4l2_fh_exit(fh); /* Easy case first: this stream was never claimed by us */ if (s->id != id->open_id) { @@ -895,6 +911,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) { struct ivtv *itv = s->itv; struct ivtv_open_id *item; + int res = 0; IVTV_DEBUG_FILE("open %s\n", s->name); @@ -915,17 +932,27 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) } /* Allocate memory */ - item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); + item = kzalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); if (NULL == item) { IVTV_DEBUG_WARN("nomem on v4l2 open\n"); return -ENOMEM; } + v4l2_fh_init(&item->fh, s->vdev); + if (s->type == IVTV_DEC_STREAM_TYPE_YUV || + s->type == IVTV_DEC_STREAM_TYPE_MPG) { + res = v4l2_event_alloc(&item->fh, 60); + } + if (res < 0) { + v4l2_fh_exit(&item->fh); + kfree(item); + return res; + } item->itv = itv; item->type = s->type; v4l2_prio_open(&itv->prio, &item->prio); item->open_id = itv->open_id++; - filp->private_data = item; + filp->private_data = &item->fh; if (item->type == IVTV_ENC_STREAM_TYPE_RAD) { /* Try to claim this stream */ @@ -940,6 +967,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) /* switching to radio while capture is in progress is not polite */ ivtv_release_stream(s); + v4l2_fh_exit(&item->fh); kfree(item); return -EBUSY; } @@ -970,6 +998,7 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); itv->yuv_info.stream_size = 0; } + v4l2_fh_add(&item->fh); return 0; } diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 2ee03c2a1b58..a5b92d109c6c 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -193,7 +193,7 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case IVTV_HW_I2C_IR_RX_AVER: - init_data->ir_codes = &ir_codes_avermedia_cardbus_table; + init_data->ir_codes = RC_MAP_AVERMEDIA_CARDBUS; init_data->internal_get_key_func = IR_KBD_GET_KEY_AVERMEDIA_CARDBUS; init_data->type = IR_TYPE_OTHER; @@ -202,14 +202,14 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) case IVTV_HW_I2C_IR_RX_HAUP_EXT: case IVTV_HW_I2C_IR_RX_HAUP_INT: /* Default to old black remote */ - init_data->ir_codes = &ir_codes_rc5_tv_table; + init_data->ir_codes = RC_MAP_RC5_TV; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; init_data->type = IR_TYPE_RC5; init_data->name = itv->card_name; break; case IVTV_HW_Z8F0811_IR_RX_HAUP: /* Default to grey remote */ - init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; init_data->type = IR_TYPE_RC5; init_data->name = itv->card_name; diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 99f3c39a118b..fa9f0d958f96 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -35,6 +35,7 @@ #include <media/saa7127.h> #include <media/tveeprom.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-event.h> #include <linux/dvb/audio.h> #include <linux/i2c-id.h> @@ -391,7 +392,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo return 0; } - v4l2_subdev_call(itv->sd_video, video, g_fmt, fmt); + v4l2_subdev_call(itv->sd_video, vbi, g_sliced_fmt, vbifmt); vbifmt->service_set = ivtv_get_service_set(vbifmt); return 0; } @@ -597,7 +598,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f return -EBUSY; itv->vbi.sliced_in->service_set = 0; itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; - v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); + v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &fmt->fmt.vbi); return ivtv_g_fmt_vbi_cap(file, fh, fmt); } @@ -615,7 +616,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0) return -EBUSY; itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); + v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, vbifmt); memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); return 0; } @@ -1087,8 +1088,10 @@ static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std) int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) { + DEFINE_WAIT(wait); struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; struct yuv_playback_info *yi = &itv->yuv_info; + int f; if ((*std & V4L2_STD_ALL) == 0) return -EINVAL; @@ -1128,6 +1131,25 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) itv->is_out_60hz = itv->is_60hz; itv->is_out_50hz = itv->is_50hz; ivtv_call_all(itv, video, s_std_output, itv->std_out); + + /* + * The next firmware call is time sensitive. Time it to + * avoid risk of a hard lock, by trying to ensure the call + * happens within the first 100 lines of the top field. + * Make 4 attempts to sync to the decoder before giving up. + */ + for (f = 0; f < 4; f++) { + prepare_to_wait(&itv->vsync_waitq, &wait, + TASK_UNINTERRUPTIBLE); + if ((read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16) < 100) + break; + schedule_timeout(msecs_to_jiffies(25)); + } + finish_wait(&itv->vsync_waitq, &wait); + + if (f == 4) + IVTV_WARN("Mode change failed to sync to decoder\n"); + ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); itv->main_rect.left = itv->main_rect.top = 0; itv->main_rect.width = 720; @@ -1431,6 +1453,18 @@ static int ivtv_overlay(struct file *file, void *fh, unsigned int on) return 0; } +static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_VSYNC: + case V4L2_EVENT_EOS: + break; + default: + return -EINVAL; + } + return v4l2_event_subscribe(fh, sub); +} + static int ivtv_log_status(struct file *file, void *fh) { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; @@ -1539,10 +1573,11 @@ static int ivtv_log_status(struct file *file, void *fh) static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) { - struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; int nonblocking = filp->f_flags & O_NONBLOCK; struct ivtv_stream *s = &itv->streams[id->type]; + unsigned long iarg = (unsigned long)arg; switch (cmd) { case IVTV_IOC_DMA_FRAME: { @@ -1724,6 +1759,33 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) break; } + case VIDEO_SELECT_SOURCE: + IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + return ivtv_passthrough_mode(itv, iarg == VIDEO_SOURCE_DEMUX); + + case AUDIO_SET_MUTE: + IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n"); + itv->speed_mute_audio = iarg; + return 0; + + case AUDIO_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); + if (iarg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_stereo_mode = iarg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + + case AUDIO_BILINGUAL_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); + if (iarg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_bilingual_mode = iarg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + default: return -EINVAL; } @@ -1755,6 +1817,10 @@ static long ivtv_default(struct file *file, void *fh, int cmd, void *arg) case VIDEO_CONTINUE: case VIDEO_COMMAND: case VIDEO_TRY_COMMAND: + case VIDEO_SELECT_SOURCE: + case AUDIO_SET_MUTE: + case AUDIO_CHANNEL_SELECT: + case AUDIO_BILINGUAL_CHANNEL_SELECT: return ivtv_decoder_ioctls(file, cmd, (void *)arg); default: @@ -1767,42 +1833,9 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, unsigned int cmd, unsigned long arg) { struct video_device *vfd = video_devdata(filp); - struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); long ret; - /* Filter dvb ioctls that cannot be handled by the v4l ioctl framework */ - switch (cmd) { - case VIDEO_SELECT_SOURCE: - IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) - return -EINVAL; - return ivtv_passthrough_mode(itv, arg == VIDEO_SOURCE_DEMUX); - - case AUDIO_SET_MUTE: - IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n"); - itv->speed_mute_audio = arg; - return 0; - - case AUDIO_CHANNEL_SELECT: - IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); - if (arg > AUDIO_STEREO_SWAPPED) - return -EINVAL; - itv->audio_stereo_mode = arg; - ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); - return 0; - - case AUDIO_BILINGUAL_CHANNEL_SELECT: - IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); - if (arg > AUDIO_STEREO_SWAPPED) - return -EINVAL; - itv->audio_bilingual_mode = arg; - ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); - return 0; - - default: - break; - } - /* check priority */ switch (cmd) { case VIDIOC_S_CTRL: @@ -1817,8 +1850,9 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, case VIDIOC_S_AUDOUT: case VIDIOC_S_EXT_CTRLS: case VIDIOC_S_FBUF: + case VIDIOC_S_PRIORITY: case VIDIOC_OVERLAY: - ret = v4l2_prio_check(&itv->prio, &id->prio); + ret = v4l2_prio_check(&itv->prio, id->prio); if (ret) return ret; } @@ -1832,10 +1866,13 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; long res; + /* DQEVENT can block, so this should not run with the serialize lock */ + if (cmd == VIDIOC_DQEVENT) + return ivtv_serialized_ioctl(itv, filp, cmd, arg); mutex_lock(&itv->serialize_lock); res = ivtv_serialized_ioctl(itv, filp, cmd, arg); mutex_unlock(&itv->serialize_lock); @@ -1906,6 +1943,8 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .vidioc_g_ext_ctrls = ivtv_g_ext_ctrls, .vidioc_s_ext_ctrls = ivtv_s_ext_ctrls, .vidioc_try_ext_ctrls = ivtv_try_ext_ctrls, + .vidioc_subscribe_event = ivtv_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; void ivtv_set_funcs(struct video_device *vdev) diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index 12d36ca91d53..fea1ec33b0df 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -25,6 +25,7 @@ #include "ivtv-mailbox.h" #include "ivtv-vbi.h" #include "ivtv-yuv.h" +#include <media/v4l2-event.h> #define DMA_MAGIC_COOKIE 0x000001fe @@ -752,7 +753,7 @@ static void ivtv_irq_vsync(struct ivtv *itv) * to determine the line being displayed and ensure we handle * one vsync per frame. */ - unsigned int frame = read_reg(0x28c0) & 1; + unsigned int frame = read_reg(IVTV_REG_DEC_LINE_FIELD) & 1; struct yuv_playback_info *yi = &itv->yuv_info; int last_dma_frame = atomic_read(&yi->next_dma_frame); struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame]; @@ -778,6 +779,14 @@ static void ivtv_irq_vsync(struct ivtv *itv) } } if (frame != (itv->last_vsync_field & 1)) { + static const struct v4l2_event evtop = { + .type = V4L2_EVENT_VSYNC, + .u.vsync.field = V4L2_FIELD_TOP, + }; + static const struct v4l2_event evbottom = { + .type = V4L2_EVENT_VSYNC, + .u.vsync.field = V4L2_FIELD_BOTTOM, + }; struct ivtv_stream *s = ivtv_get_output_stream(itv); itv->last_vsync_field += 1; @@ -791,10 +800,12 @@ static void ivtv_irq_vsync(struct ivtv *itv) if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) { set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags); wake_up(&itv->event_waitq); + if (s) + wake_up(&s->waitq); } + if (s && s->vdev) + v4l2_event_queue(s->vdev, frame ? &evtop : &evbottom); wake_up(&itv->vsync_waitq); - if (s) - wake_up(&s->waitq); /* Send VBI to saa7127 */ if (frame && (itv->output_mode == OUT_PASSTHROUGH || @@ -852,9 +863,11 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) */ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { /* vsync is enabled, see if we're in a new field */ - if ((itv->last_vsync_field & 1) != (read_reg(0x28c0) & 1)) { + if ((itv->last_vsync_field & 1) != + (read_reg(IVTV_REG_DEC_LINE_FIELD) & 1)) { /* New field, looks like we missed it */ - IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16); + IVTV_DEBUG_YUV("VSync interrupt missed %d\n", + read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16); vsync_force = 1; } } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 1f9387f6ca24..de4288cc1889 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -42,6 +42,7 @@ #include "ivtv-yuv.h" #include "ivtv-cards.h" #include "ivtv-streams.h" +#include <media/v4l2-event.h> static const struct v4l2_file_operations ivtv_v4l2_enc_fops = { .owner = THIS_MODULE, @@ -343,7 +344,10 @@ static void ivtv_vbi_setup(struct ivtv *itv) ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); /* setup VBI registers */ - v4l2_subdev_call(itv->sd_video, video, s_fmt, &itv->vbi.in); + if (raw) + v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &itv->vbi.in.fmt.vbi); + else + v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, &itv->vbi.in.fmt.sliced); /* determine number of lines and total number of VBI bytes. A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 @@ -581,10 +585,9 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1); /* Avoid unpredictable PCI bus hang - disable video clocks */ v4l2_subdev_call(itv->sd_video, video, s_stream, 0); - ivtv_msleep_timeout(150, 1); + ivtv_msleep_timeout(300, 1); ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); v4l2_subdev_call(itv->sd_video, video, s_stream, 1); - ivtv_msleep_timeout(150, 1); } /* begin_capture */ @@ -830,6 +833,10 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); } + /* Raw-passthrough is implied on start. Make sure it's stopped so + the encoder will re-initialize when next started */ + ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 7); + wake_up(&s->waitq); return 0; @@ -837,6 +844,9 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) { + static const struct v4l2_event ev = { + .type = V4L2_EVENT_EOS, + }; struct ivtv *itv = s->itv; if (s->vdev == NULL) @@ -888,6 +898,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags); wake_up(&itv->event_waitq); + v4l2_event_queue(s->vdev, &ev); /* wake up wait queues */ wake_up(&s->waitq); diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c index f420d31b937d..e1c347e5ebd8 100644 --- a/drivers/media/video/ivtv/ivtv-vbi.c +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -38,7 +38,7 @@ static void ivtv_set_vps(struct ivtv *itv, int enabled) data.data[9] = itv->vbi.vps_payload.data[2]; data.data[10] = itv->vbi.vps_payload.data[3]; data.data[11] = itv->vbi.vps_payload.data[4]; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); } static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) @@ -52,12 +52,12 @@ static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) data.line = (mode & 1) ? 21 : 0; data.data[0] = cc->odd[0]; data.data[1] = cc->odd[1]; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); data.field = 1; data.line = (mode & 2) ? 21 : 0; data.data[0] = cc->even[0]; data.data[1] = cc->even[1]; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); } static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) @@ -80,7 +80,7 @@ static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) data.line = enabled ? 23 : 0; data.data[0] = mode & 0xff; data.data[1] = (mode >> 8) & 0xff; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); } static int odd_parity(u8 c) @@ -134,7 +134,7 @@ void ivtv_write_vbi(struct ivtv *itv, const struct v4l2_sliced_vbi_data *sliced, } } } - if (found_cc && vi->cc_payload_idx < sizeof(vi->cc_payload)) { + if (found_cc && vi->cc_payload_idx < ARRAY_SIZE(vi->cc_payload)) { vi->cc_payload[vi->cc_payload_idx++] = cc; set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); } @@ -316,7 +316,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 continue; } vbi.p = p + 4; - v4l2_subdev_call(itv->sd_video, video, decode_vbi_line, &vbi); + v4l2_subdev_call(itv->sd_video, vbi, decode_vbi_line, &vbi); if (vbi.type && !(lines & (1 << vbi.line))) { lines |= 1 << vbi.line; itv->vbi.sliced_data[line].id = vbi.type; @@ -440,7 +440,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_WSS_625; data.field = 0; - if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { ivtv_set_wss(itv, 1, data.data[0] & 0xf); vi->wss_missing_cnt = 0; } else if (vi->wss_missing_cnt == 4) { @@ -454,13 +454,13 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_CAPTION_525; data.field = 0; - if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { mode |= 1; cc.odd[0] = data.data[0]; cc.odd[1] = data.data[1]; } data.field = 1; - if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { mode |= 2; cc.even[0] = data.data[0]; cc.even[1] = data.data[1]; diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index de2ff1c6ac34..49e1a283ed36 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -460,7 +460,7 @@ static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long ar vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VSYNC; - trace = read_reg(0x028c0) >> 16; + trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16; if (itv->is_50hz && trace > 312) trace -= 312; else if (itv->is_60hz && trace > 262) diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c new file mode 100644 index 000000000000..554eaf140128 --- /dev/null +++ b/drivers/media/video/mem2mem_testdev.c @@ -0,0 +1,1052 @@ +/* + * A virtual v4l2-mem2mem example device. + * + * This is a virtual device driver for testing mem-to-mem videobuf framework. + * It simulates a device that uses memory buffers for both source and + * destination, processes the data and issues an "irq" (simulated by a timer). + * The device is capable of multi-instance, multi-buffer-per-transaction + * operation (via the mem2mem framework). + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <p.osciak@samsung.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + * + * 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 + */ +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/version.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <linux/platform_device.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf-vmalloc.h> + +#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev" + +MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); +MODULE_AUTHOR("Pawel Osciak, <p.osciak@samsung.com>"); +MODULE_LICENSE("GPL"); + + +#define MIN_W 32 +#define MIN_H 32 +#define MAX_W 640 +#define MAX_H 480 +#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */ + +/* Flags that indicate a format can be used for capture/output */ +#define MEM2MEM_CAPTURE (1 << 0) +#define MEM2MEM_OUTPUT (1 << 1) + +#define MEM2MEM_NAME "m2m-testdev" + +/* Per queue */ +#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME +/* In bytes, per queue */ +#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024) + +/* Default transaction time in msec */ +#define MEM2MEM_DEF_TRANSTIME 1000 +/* Default number of buffers per transaction */ +#define MEM2MEM_DEF_TRANSLEN 1 +#define MEM2MEM_COLOR_STEP (0xff >> 4) +#define MEM2MEM_NUM_TILES 8 + +#define dprintk(dev, fmt, arg...) \ + v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + + +void m2mtest_dev_release(struct device *dev) +{} + +static struct platform_device m2mtest_pdev = { + .name = MEM2MEM_NAME, + .dev.release = m2mtest_dev_release, +}; + +struct m2mtest_fmt { + char *name; + u32 fourcc; + int depth; + /* Types the format can be used for */ + u32 types; +}; + +static struct m2mtest_fmt formats[] = { + { + .name = "RGB565 (BE)", + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .depth = 16, + /* Both capture and output format */ + .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, + }, + { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + /* Output-only format */ + .types = MEM2MEM_OUTPUT, + }, +}; + +/* Per-queue, driver-specific private data */ +struct m2mtest_q_data { + unsigned int width; + unsigned int height; + unsigned int sizeimage; + struct m2mtest_fmt *fmt; +}; + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +/* Source and destination queue data */ +static struct m2mtest_q_data q_data[2]; + +static struct m2mtest_q_data *get_q_data(enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &q_data[V4L2_M2M_SRC]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &q_data[V4L2_M2M_DST]; + default: + BUG(); + } + return NULL; +} + +#define V4L2_CID_TRANS_TIME_MSEC V4L2_CID_PRIVATE_BASE +#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_PRIVATE_BASE + 1) + +static struct v4l2_queryctrl m2mtest_ctrls[] = { + { + .id = V4L2_CID_TRANS_TIME_MSEC, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Transaction time (msec)", + .minimum = 1, + .maximum = 10000, + .step = 100, + .default_value = 1000, + .flags = 0, + }, { + .id = V4L2_CID_TRANS_NUM_BUFS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Buffers per transaction", + .minimum = 1, + .maximum = MEM2MEM_DEF_NUM_BUFS, + .step = 1, + .default_value = 1, + .flags = 0, + }, +}; + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static struct m2mtest_fmt *find_format(struct v4l2_format *f) +{ + struct m2mtest_fmt *fmt; + unsigned int k; + + for (k = 0; k < NUM_FORMATS; k++) { + fmt = &formats[k]; + if (fmt->fourcc == f->fmt.pix.pixelformat) + break; + } + + if (k == NUM_FORMATS) + return NULL; + + return &formats[k]; +} + +struct m2mtest_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd; + + atomic_t num_inst; + struct mutex dev_mutex; + spinlock_t irqlock; + + struct timer_list timer; + + struct v4l2_m2m_dev *m2m_dev; +}; + +struct m2mtest_ctx { + struct m2mtest_dev *dev; + + /* Processed buffers in this transaction */ + u8 num_processed; + + /* Transaction length (i.e. how many buffers per transaction) */ + u32 translen; + /* Transaction time (i.e. simulated processing time) in milliseconds */ + u32 transtime; + + /* Abort requested by m2m */ + int aborting; + + struct v4l2_m2m_ctx *m2m_ctx; +}; + +struct m2mtest_buffer { + /* vb must be first! */ + struct videobuf_buffer vb; +}; + +static struct v4l2_queryctrl *get_ctrl(int id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(m2mtest_ctrls); ++i) { + if (id == m2mtest_ctrls[i].id) + return &m2mtest_ctrls[i]; + } + + return NULL; +} + +static int device_process(struct m2mtest_ctx *ctx, + struct m2mtest_buffer *in_buf, + struct m2mtest_buffer *out_buf) +{ + struct m2mtest_dev *dev = ctx->dev; + u8 *p_in, *p_out; + int x, y, t, w; + int tile_w, bytes_left; + struct videobuf_queue *src_q; + struct videobuf_queue *dst_q; + + src_q = v4l2_m2m_get_src_vq(ctx->m2m_ctx); + dst_q = v4l2_m2m_get_dst_vq(ctx->m2m_ctx); + p_in = videobuf_queue_to_vaddr(src_q, &in_buf->vb); + p_out = videobuf_queue_to_vaddr(dst_q, &out_buf->vb); + if (!p_in || !p_out) { + v4l2_err(&dev->v4l2_dev, + "Acquiring kernel pointers to buffers failed\n"); + return -EFAULT; + } + + if (in_buf->vb.size < out_buf->vb.size) { + v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n"); + return -EINVAL; + } + + tile_w = (in_buf->vb.width * (q_data[V4L2_M2M_DST].fmt->depth >> 3)) + / MEM2MEM_NUM_TILES; + bytes_left = in_buf->vb.bytesperline - tile_w * MEM2MEM_NUM_TILES; + w = 0; + + for (y = 0; y < in_buf->vb.height; ++y) { + for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { + if (w & 0x1) { + for (x = 0; x < tile_w; ++x) + *p_out++ = *p_in++ + MEM2MEM_COLOR_STEP; + } else { + for (x = 0; x < tile_w; ++x) + *p_out++ = *p_in++ - MEM2MEM_COLOR_STEP; + } + ++w; + } + p_in += bytes_left; + p_out += bytes_left; + } + + return 0; +} + +static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout) +{ + dprintk(dev, "Scheduling a simulated irq\n"); + mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout)); +} + +/* + * mem2mem callbacks + */ + +/** + * job_ready() - check whether an instance is ready to be scheduled to run + */ +static int job_ready(void *priv) +{ + struct m2mtest_ctx *ctx = priv; + + if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen + || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) { + dprintk(ctx->dev, "Not enough buffers available\n"); + return 0; + } + + return 1; +} + +static void job_abort(void *priv) +{ + struct m2mtest_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + ctx->aborting = 1; +} + +/* device_run() - prepares and starts the device + * + * This simulates all the immediate preparations required before starting + * a device. This will be called by the framework when it decides to schedule + * a particular instance. + */ +static void device_run(void *priv) +{ + struct m2mtest_ctx *ctx = priv; + struct m2mtest_dev *dev = ctx->dev; + struct m2mtest_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + + device_process(ctx, src_buf, dst_buf); + + /* Run a timer, which simulates a hardware irq */ + schedule_irq(dev, ctx->transtime); +} + + +static void device_isr(unsigned long priv) +{ + struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv; + struct m2mtest_ctx *curr_ctx; + struct m2mtest_buffer *src_buf, *dst_buf; + unsigned long flags; + + curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev); + + if (NULL == curr_ctx) { + printk(KERN_ERR + "Instance released before the end of transaction\n"); + return; + } + + src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); + curr_ctx->num_processed++; + + if (curr_ctx->num_processed == curr_ctx->translen + || curr_ctx->aborting) { + dprintk(curr_ctx->dev, "Finishing transaction\n"); + curr_ctx->num_processed = 0; + spin_lock_irqsave(&m2mtest_dev->irqlock, flags); + src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE; + wake_up(&src_buf->vb.done); + wake_up(&dst_buf->vb.done); + spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags); + v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx); + } else { + spin_lock_irqsave(&m2mtest_dev->irqlock, flags); + src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE; + wake_up(&src_buf->vb.done); + wake_up(&dst_buf->vb.done); + spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags); + device_run(curr_ctx); + } +} + + +/* + * video ioctls + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(0, 1, 0); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING; + + return 0; +} + +static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) +{ + int i, num; + struct m2mtest_fmt *fmt; + + num = 0; + + for (i = 0; i < NUM_FORMATS; ++i) { + if (formats[i].types & type) { + /* index-th format of type type found ? */ + if (num == f->index) + break; + /* Correct type but haven't reached our index yet, + * just increment per-type index */ + ++num; + } + } + + if (i < NUM_FORMATS) { + /* Format found */ + fmt = &formats[i]; + strncpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + return 0; + } + + /* Format not found */ + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return enum_fmt(f, MEM2MEM_CAPTURE); +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return enum_fmt(f, MEM2MEM_OUTPUT); +} + +static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) +{ + struct videobuf_queue *vq; + struct m2mtest_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(f->type); + + f->fmt.pix.width = q_data->width; + f->fmt.pix.height = q_data->height; + f->fmt.pix.field = vq->field; + f->fmt.pix.pixelformat = q_data->fmt->fourcc; + f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; + f->fmt.pix.sizeimage = q_data->sizeimage; + + return 0; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(priv, f); +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(priv, f); +} + +static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt) +{ + enum v4l2_field field; + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) + field = V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE != field) + return -EINVAL; + + /* V4L2 specification suggests the driver corrects the format struct + * if any of the dimensions is unsupported */ + f->fmt.pix.field = field; + + if (f->fmt.pix.height < MIN_H) + f->fmt.pix.height = MIN_H; + else if (f->fmt.pix.height > MAX_H) + f->fmt.pix.height = MAX_H; + + if (f->fmt.pix.width < MIN_W) + f->fmt.pix.width = MIN_W; + else if (f->fmt.pix.width > MAX_W) + f->fmt.pix.width = MAX_W; + + f->fmt.pix.width &= ~DIM_ALIGN_MASK; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct m2mtest_fmt *fmt; + struct m2mtest_ctx *ctx = priv; + + fmt = find_format(f); + if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct m2mtest_fmt *fmt; + struct m2mtest_ctx *ctx = priv; + + fmt = find_format(f); + if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) +{ + struct m2mtest_q_data *q_data; + struct videobuf_queue *vq; + int ret = 0; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(f->type); + if (!q_data) + return -EINVAL; + + mutex_lock(&vq->vb_lock); + + if (videobuf_queue_is_busy(vq)) { + v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + q_data->fmt = find_format(f); + q_data->width = f->fmt.pix.width; + q_data->height = f->fmt.pix.height; + q_data->sizeimage = q_data->width * q_data->height + * q_data->fmt->depth >> 3; + vq->field = f->fmt.pix.field; + + dprintk(ctx->dev, + "Setting format for type %d, wxh: %dx%d, fmt: %d\n", + f->type, q_data->width, q_data->height, q_data->fmt->fourcc); + +out: + mutex_unlock(&vq->vb_lock); + return ret; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(priv, f); +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = vidioc_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(priv, f); +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct m2mtest_ctx *ctx = priv; + + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct v4l2_queryctrl *c; + + c = get_ctrl(qc->id); + if (!c) + return -EINVAL; + + *qc = *c; + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct m2mtest_ctx *ctx = priv; + + switch (ctrl->id) { + case V4L2_CID_TRANS_TIME_MSEC: + ctrl->value = ctx->transtime; + break; + + case V4L2_CID_TRANS_NUM_BUFS: + ctrl->value = ctx->translen; + break; + + default: + v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n"); + return -EINVAL; + } + + return 0; +} + +static int check_ctrl_val(struct m2mtest_ctx *ctx, struct v4l2_control *ctrl) +{ + struct v4l2_queryctrl *c; + + c = get_ctrl(ctrl->id); + if (!c) + return -EINVAL; + + if (ctrl->value < c->minimum || ctrl->value > c->maximum) { + v4l2_err(&ctx->dev->v4l2_dev, "Value out of range\n"); + return -ERANGE; + } + + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct m2mtest_ctx *ctx = priv; + int ret = 0; + + ret = check_ctrl_val(ctx, ctrl); + if (ret != 0) + return ret; + + switch (ctrl->id) { + case V4L2_CID_TRANS_TIME_MSEC: + ctx->transtime = ctrl->value; + break; + + case V4L2_CID_TRANS_NUM_BUFS: + ctx->translen = ctrl->value; + break; + + default: + v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n"); + return -EINVAL; + } + + return 0; +} + + +static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; + + +/* + * Queue operations + */ + +static void m2mtest_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct m2mtest_ctx *ctx = vq->priv_data; + + dprintk(ctx->dev, "type: %d, index: %d, state: %d\n", + vq->type, vb->i, vb->state); + + videobuf_vmalloc_free(vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static int m2mtest_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct m2mtest_ctx *ctx = vq->priv_data; + struct m2mtest_q_data *q_data; + + q_data = get_q_data(vq->type); + + *size = q_data->width * q_data->height * q_data->fmt->depth >> 3; + dprintk(ctx->dev, "size:%d, w/h %d/%d, depth: %d\n", + *size, q_data->width, q_data->height, q_data->fmt->depth); + + if (0 == *count) + *count = MEM2MEM_DEF_NUM_BUFS; + + while (*size * *count > MEM2MEM_VID_MEM_LIMIT) + (*count)--; + + v4l2_info(&ctx->dev->v4l2_dev, + "%d buffers of size %d set up.\n", *count, *size); + + return 0; +} + +static int m2mtest_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct m2mtest_ctx *ctx = vq->priv_data; + struct m2mtest_q_data *q_data; + int ret; + + dprintk(ctx->dev, "type: %d, index: %d, state: %d\n", + vq->type, vb->i, vb->state); + + q_data = get_q_data(vq->type); + + if (vb->baddr) { + /* User-provided buffer */ + if (vb->bsize < q_data->sizeimage) { + /* Buffer too small to fit a frame */ + v4l2_err(&ctx->dev->v4l2_dev, + "User-provided buffer too small\n"); + return -EINVAL; + } + } else if (vb->state != VIDEOBUF_NEEDS_INIT + && vb->bsize < q_data->sizeimage) { + /* We provide the buffer, but it's already been initialized + * and is too small */ + return -EINVAL; + } + + vb->width = q_data->width; + vb->height = q_data->height; + vb->bytesperline = (q_data->width * q_data->fmt->depth) >> 3; + vb->size = q_data->sizeimage; + vb->field = field; + + if (VIDEOBUF_NEEDS_INIT == vb->state) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) { + v4l2_err(&ctx->dev->v4l2_dev, + "Iolock failed\n"); + goto fail; + } + } + + vb->state = VIDEOBUF_PREPARED; + + return 0; +fail: + m2mtest_buf_release(vq, vb); + return ret; +} + +static void m2mtest_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct m2mtest_ctx *ctx = vq->priv_data; + + v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb); +} + +static struct videobuf_queue_ops m2mtest_qops = { + .buf_setup = m2mtest_buf_setup, + .buf_prepare = m2mtest_buf_prepare, + .buf_queue = m2mtest_buf_queue, + .buf_release = m2mtest_buf_release, +}; + +static void queue_init(void *priv, struct videobuf_queue *vq, + enum v4l2_buf_type type) +{ + struct m2mtest_ctx *ctx = priv; + + videobuf_queue_vmalloc_init(vq, &m2mtest_qops, ctx->dev->v4l2_dev.dev, + &ctx->dev->irqlock, type, V4L2_FIELD_NONE, + sizeof(struct m2mtest_buffer), priv); +} + + +/* + * File operations + */ +static int m2mtest_open(struct file *file) +{ + struct m2mtest_dev *dev = video_drvdata(file); + struct m2mtest_ctx *ctx = NULL; + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + file->private_data = ctx; + ctx->dev = dev; + ctx->translen = MEM2MEM_DEF_TRANSLEN; + ctx->transtime = MEM2MEM_DEF_TRANSTIME; + ctx->num_processed = 0; + + ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, dev->m2m_dev, queue_init); + if (IS_ERR(ctx->m2m_ctx)) { + int ret = PTR_ERR(ctx->m2m_ctx); + + kfree(ctx); + return ret; + } + + atomic_inc(&dev->num_inst); + + dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); + + return 0; +} + +static int m2mtest_release(struct file *file) +{ + struct m2mtest_dev *dev = video_drvdata(file); + struct m2mtest_ctx *ctx = file->private_data; + + dprintk(dev, "Releasing instance %p\n", ctx); + + v4l2_m2m_ctx_release(ctx->m2m_ctx); + kfree(ctx); + + atomic_dec(&dev->num_inst); + + return 0; +} + +static unsigned int m2mtest_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct m2mtest_ctx *ctx = (struct m2mtest_ctx *)file->private_data; + + return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); +} + +static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct m2mtest_ctx *ctx = (struct m2mtest_ctx *)file->private_data; + + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); +} + +static const struct v4l2_file_operations m2mtest_fops = { + .owner = THIS_MODULE, + .open = m2mtest_open, + .release = m2mtest_release, + .poll = m2mtest_poll, + .ioctl = video_ioctl2, + .mmap = m2mtest_mmap, +}; + +static struct video_device m2mtest_videodev = { + .name = MEM2MEM_NAME, + .fops = &m2mtest_fops, + .ioctl_ops = &m2mtest_ioctl_ops, + .minor = -1, + .release = video_device_release, +}; + +static struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, + .job_ready = job_ready, + .job_abort = job_abort, +}; + +static int m2mtest_probe(struct platform_device *pdev) +{ + struct m2mtest_dev *dev; + struct video_device *vfd; + int ret; + + dev = kzalloc(sizeof *dev, GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->irqlock); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + goto free_dev; + + atomic_set(&dev->num_inst, 0); + mutex_init(&dev->dev_mutex); + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); + ret = -ENOMEM; + goto unreg_dev; + } + + *vfd = m2mtest_videodev; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto rel_vdev; + } + + video_set_drvdata(vfd, dev); + snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name); + dev->vfd = vfd; + v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME + "Device registered as /dev/video%d\n", vfd->num); + + setup_timer(&dev->timer, device_isr, (long)dev); + platform_set_drvdata(pdev, dev); + + dev->m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(dev->m2m_dev); + goto err_m2m; + } + + return 0; + +err_m2m: + video_unregister_device(dev->vfd); +rel_vdev: + video_device_release(vfd); +unreg_dev: + v4l2_device_unregister(&dev->v4l2_dev); +free_dev: + kfree(dev); + + return ret; +} + +static int m2mtest_remove(struct platform_device *pdev) +{ + struct m2mtest_dev *dev = + (struct m2mtest_dev *)platform_get_drvdata(pdev); + + v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME); + v4l2_m2m_release(dev->m2m_dev); + del_timer_sync(&dev->timer); + video_unregister_device(dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + + return 0; +} + +static struct platform_driver m2mtest_pdrv = { + .probe = m2mtest_probe, + .remove = m2mtest_remove, + .driver = { + .name = MEM2MEM_NAME, + .owner = THIS_MODULE, + }, +}; + +static void __exit m2mtest_exit(void) +{ + platform_driver_unregister(&m2mtest_pdrv); + platform_device_unregister(&m2mtest_pdev); +} + +static int __init m2mtest_init(void) +{ + int ret; + + ret = platform_device_register(&m2mtest_pdev); + if (ret) + return ret; + + ret = platform_driver_register(&m2mtest_pdrv); + if (ret) + platform_device_unregister(&m2mtest_pdev); + + return 0; +} + +module_init(m2mtest_init); +module_exit(m2mtest_exit); + diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 4404e5ef818f..2be23bccd3c8 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -30,9 +30,10 @@ #include <linux/pci.h> #include <linux/sched.h> #include <linux/init.h> -#include <linux/videodev.h> #include <linux/gfp.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -1168,22 +1169,22 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) case V4L2_CID_BRIGHTNESS: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); - meye.picture.brightness = c->value << 10; + meye.brightness = c->value << 10; break; case V4L2_CID_HUE: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERAHUE, c->value); - meye.picture.hue = c->value << 10; + meye.hue = c->value << 10; break; case V4L2_CID_CONTRAST: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); - meye.picture.contrast = c->value << 10; + meye.contrast = c->value << 10; break; case V4L2_CID_SATURATION: sony_pic_camera_command( SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); - meye.picture.colour = c->value << 10; + meye.colour = c->value << 10; break; case V4L2_CID_AGC: sony_pic_camera_command( @@ -1221,16 +1222,16 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) mutex_lock(&meye.lock); switch (c->id) { case V4L2_CID_BRIGHTNESS: - c->value = meye.picture.brightness >> 10; + c->value = meye.brightness >> 10; break; case V4L2_CID_HUE: - c->value = meye.picture.hue >> 10; + c->value = meye.hue >> 10; break; case V4L2_CID_CONTRAST: - c->value = meye.picture.contrast >> 10; + c->value = meye.contrast >> 10; break; case V4L2_CID_SATURATION: - c->value = meye.picture.colour >> 10; + c->value = meye.colour >> 10; break; case V4L2_CID_AGC: c->value = meye.params.agc; @@ -1729,6 +1730,7 @@ static int meye_resume(struct pci_dev *pdev) static int __devinit meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { + struct v4l2_device *v4l2_dev = &meye.v4l2_dev; int ret = -EBUSY; unsigned long mchip_adr; @@ -1737,70 +1739,75 @@ static int __devinit meye_probe(struct pci_dev *pcidev, goto outnotdev; } + ret = v4l2_device_register(&pcidev->dev, v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return ret; + } ret = -ENOMEM; meye.mchip_dev = pcidev; - meye.video_dev = video_device_alloc(); - if (!meye.video_dev) { - printk(KERN_ERR "meye: video_device_alloc() failed!\n"); + meye.vdev = video_device_alloc(); + if (!meye.vdev) { + v4l2_err(v4l2_dev, "video_device_alloc() failed!\n"); goto outnotdev; } meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE); if (!meye.grab_temp) { - printk(KERN_ERR "meye: grab buffer allocation failed\n"); + v4l2_err(v4l2_dev, "grab buffer allocation failed\n"); goto outvmalloc; } spin_lock_init(&meye.grabq_lock); if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS, GFP_KERNEL)) { - printk(KERN_ERR "meye: fifo allocation failed\n"); + v4l2_err(v4l2_dev, "fifo allocation failed\n"); goto outkfifoalloc1; } spin_lock_init(&meye.doneq_lock); if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS, GFP_KERNEL)) { - printk(KERN_ERR "meye: fifo allocation failed\n"); + v4l2_err(v4l2_dev, "fifo allocation failed\n"); goto outkfifoalloc2; } - memcpy(meye.video_dev, &meye_template, sizeof(meye_template)); - meye.video_dev->parent = &meye.mchip_dev->dev; + memcpy(meye.vdev, &meye_template, sizeof(meye_template)); + meye.vdev->v4l2_dev = &meye.v4l2_dev; ret = -EIO; if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { - printk(KERN_ERR "meye: unable to power on the camera\n"); - printk(KERN_ERR "meye: did you enable the camera in " + v4l2_err(v4l2_dev, "meye: unable to power on the camera\n"); + v4l2_err(v4l2_dev, "meye: did you enable the camera in " "sonypi using the module options ?\n"); goto outsonypienable; } if ((ret = pci_enable_device(meye.mchip_dev))) { - printk(KERN_ERR "meye: pci_enable_device failed\n"); + v4l2_err(v4l2_dev, "meye: pci_enable_device failed\n"); goto outenabledev; } mchip_adr = pci_resource_start(meye.mchip_dev,0); if (!mchip_adr) { - printk(KERN_ERR "meye: mchip has no device base address\n"); + v4l2_err(v4l2_dev, "meye: mchip has no device base address\n"); goto outregions; } if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0), pci_resource_len(meye.mchip_dev, 0), "meye")) { - printk(KERN_ERR "meye: request_mem_region failed\n"); + v4l2_err(v4l2_dev, "meye: request_mem_region failed\n"); goto outregions; } meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS); if (!meye.mchip_mmregs) { - printk(KERN_ERR "meye: ioremap failed\n"); + v4l2_err(v4l2_dev, "meye: ioremap failed\n"); goto outremap; } meye.mchip_irq = pcidev->irq; if (request_irq(meye.mchip_irq, meye_irq, IRQF_DISABLED | IRQF_SHARED, "meye", meye_irq)) { - printk(KERN_ERR "meye: request_irq failed\n"); + v4l2_err(v4l2_dev, "request_irq failed\n"); goto outreqirq; } @@ -1824,21 +1831,18 @@ static int __devinit meye_probe(struct pci_dev *pcidev, msleep(1); mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); - if (video_register_device(meye.video_dev, VFL_TYPE_GRABBER, + if (video_register_device(meye.vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - printk(KERN_ERR "meye: video_register_device failed\n"); + v4l2_err(v4l2_dev, "video_register_device failed\n"); goto outvideoreg; } mutex_init(&meye.lock); init_waitqueue_head(&meye.proc_list); - meye.picture.depth = 16; - meye.picture.palette = VIDEO_PALETTE_YUV422; - meye.picture.brightness = 32 << 10; - meye.picture.hue = 32 << 10; - meye.picture.colour = 32 << 10; - meye.picture.contrast = 32 << 10; - meye.picture.whiteness = 0; + meye.brightness = 32 << 10; + meye.hue = 32 << 10; + meye.colour = 32 << 10; + meye.contrast = 32 << 10; meye.params.subsample = 0; meye.params.quality = 8; meye.params.sharpness = 32; @@ -1854,9 +1858,9 @@ static int __devinit meye_probe(struct pci_dev *pcidev, sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0); sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48); - printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n", + v4l2_info(v4l2_dev, "Motion Eye Camera Driver v%s.\n", MEYE_DRIVER_VERSION); - printk(KERN_INFO "meye: mchip KL5A72002 rev. %d, base %lx, irq %d\n", + v4l2_info(v4l2_dev, "mchip KL5A72002 rev. %d, base %lx, irq %d\n", meye.mchip_dev->revision, mchip_adr, meye.mchip_irq); return 0; @@ -1879,14 +1883,14 @@ outkfifoalloc2: outkfifoalloc1: vfree(meye.grab_temp); outvmalloc: - video_device_release(meye.video_dev); + video_device_release(meye.vdev); outnotdev: return ret; } static void __devexit meye_remove(struct pci_dev *pcidev) { - video_unregister_device(meye.video_dev); + video_unregister_device(meye.vdev); mchip_hic_stop(); diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h index 1321ad5d6597..4bdeb03f1644 100644 --- a/drivers/media/video/meye.h +++ b/drivers/media/video/meye.h @@ -31,7 +31,7 @@ #define _MEYE_PRIV_H_ #define MEYE_DRIVER_MAJORVERSION 1 -#define MEYE_DRIVER_MINORVERSION 13 +#define MEYE_DRIVER_MINORVERSION 14 #define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \ __stringify(MEYE_DRIVER_MINORVERSION) @@ -289,6 +289,7 @@ struct meye_grab_buffer { /* Motion Eye device structure */ struct meye { + struct v4l2_device v4l2_dev; /* Main v4l2_device struct */ struct pci_dev *mchip_dev; /* pci device */ u8 mchip_irq; /* irq */ u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ @@ -308,8 +309,11 @@ struct meye { struct kfifo doneq; /* queue for grabbed buffers */ spinlock_t doneq_lock; /* lock protecting the queue */ wait_queue_head_t proc_list; /* wait queue */ - struct video_device *video_dev; /* video device parameters */ - struct video_picture picture; /* video picture parameters */ + struct video_device *vdev; /* video device parameters */ + u16 brightness; + u16 hue; + u16 contrast; + u16 colour; struct meye_params params; /* additional parameters */ unsigned long in_use; /* set to 1 if the device is in use */ #ifdef CONFIG_PM diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index a9061bff79b2..78b4e091d2d5 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -8,14 +8,16 @@ * published by the Free Software Foundation. */ -#include <linux/videodev2.h> -#include <linux/slab.h> +#include <linux/device.h> #include <linux/i2c.h> #include <linux/log2.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/videodev2.h> -#include <media/v4l2-subdev.h> -#include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-subdev.h> /* * mt9t031 i2c address 0x5d @@ -681,12 +683,66 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) } /* + * Power Management: + * This function does nothing for now but must be present for pm to work + */ +static int mt9t031_runtime_suspend(struct device *dev) +{ + return 0; +} + +/* + * Power Management: + * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged + * they are however changed at reset if the platform hook is present + * thus we rewrite them with the values stored by the driver + */ +static int mt9t031_runtime_resume(struct device *dev) +{ + struct video_device *vdev = to_video_device(dev); + struct soc_camera_device *icd = container_of(vdev->parent, + struct soc_camera_device, dev); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + + int ret; + u16 xbin, ybin; + + xbin = min(mt9t031->xskip, (u16)3); + ybin = min(mt9t031->yskip, (u16)3); + + ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, + ((xbin - 1) << 4) | (mt9t031->xskip - 1)); + if (ret < 0) + return ret; + + ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, + ((ybin - 1) << 4) | (mt9t031->yskip - 1)); + if (ret < 0) + return ret; + + return 0; +} + +static struct dev_pm_ops mt9t031_dev_pm_ops = { + .runtime_suspend = mt9t031_runtime_suspend, + .runtime_resume = mt9t031_runtime_resume, +}; + +static struct device_type mt9t031_dev_type = { + .name = "MT9T031", + .pm = &mt9t031_dev_pm_ops, +}; + +/* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ static int mt9t031_video_probe(struct i2c_client *client) { struct mt9t031 *mt9t031 = to_mt9t031(client); + struct video_device *vdev = soc_camera_i2c_to_vdev(client); s32 data; int ret; @@ -712,6 +768,8 @@ static int mt9t031_video_probe(struct i2c_client *client) ret = mt9t031_idle(client); if (ret < 0) dev_err(&client->dev, "Failed to initialise the camera\n"); + else + vdev->dev.type = &mt9t031_dev_type; /* mt9t031_idle() has reset the chip to default. */ mt9t031->exposure = 255; diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 1a34d2993e94..e5bae4c9393b 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -325,7 +325,7 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) if (ret < 0) return ret; - dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect.width, rect.height); + dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); mt9v022->rect = rect; diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 34a66019190e..5c17f9ec3d7c 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -139,8 +139,8 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (!*count) *count = 32; - while (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - (*count)--; + if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index bd297f567dc7..d477e3058002 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -796,7 +796,7 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) * FIXME: learn to use stride != width, then we can keep stride properly aligned * and support arbitrary (even) widths. */ -static inline void stride_align(__s32 *width) +static inline void stride_align(__u32 *width) { if (((*width + 7) & ~7) < 4096) *width = (*width + 7) & ~7; @@ -844,7 +844,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd, * So far only direct camera-to-memory is supported */ if (channel_change_requested(icd, rect)) { - int ret = acquire_dma_channel(mx3_cam); + ret = acquire_dma_channel(mx3_cam); if (ret < 0) return ret; } diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/video/omap/Kconfig new file mode 100644 index 000000000000..97c53949ca89 --- /dev/null +++ b/drivers/media/video/omap/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_OMAP2_VOUT + tristate "OMAP2/OMAP3 V4L2-Display driver" + depends on ARCH_OMAP24XX || ARCH_OMAP34XX + select VIDEOBUF_GEN + select VIDEOBUF_DMA_SG + select OMAP2_DSS + select OMAP2_VRAM + select OMAP2_VRFB + default n + ---help--- + V4L2 Display driver support for OMAP2/3 based boards. diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile new file mode 100644 index 000000000000..b8bab00ad010 --- /dev/null +++ b/drivers/media/video/omap/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the omap video device drivers. +# + +# OMAP2/3 Display driver +omap-vout-mod-objs := omap_vout.o omap_voutlib.o +obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout-mod.o diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c new file mode 100644 index 000000000000..4c0ab499228b --- /dev/null +++ b/drivers/media/video/omap/omap_vout.c @@ -0,0 +1,2643 @@ +/* + * omap_vout.c + * + * Copyright (C) 2005-2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Leveraged code from the OMAP2 camera driver + * Video-for-Linux (Version 2) camera capture driver for + * the OMAP24xx camera controller. + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2010 Texas Instruments. + * + * History: + * 20-APR-2006 Khasim Modified VRFB based Rotation, + * The image data is always read from 0 degree + * view and written + * to the virtual space of desired rotation angle + * 4-DEC-2006 Jian Changed to support better memory management + * + * 17-Nov-2008 Hardik Changed driver to use video_ioctl2 + * + * 23-Feb-2010 Vaibhav H Modified to use new DSS2 interface + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/irq.h> +#include <linux/videodev2.h> + +#include <media/videobuf-dma-sg.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> + +#include <plat/dma.h> +#include <plat/vram.h> +#include <plat/vrfb.h> +#include <plat/display.h> + +#include "omap_voutlib.h" +#include "omap_voutdef.h" + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP Video for Linux Video out driver"); +MODULE_LICENSE("GPL"); + + +/* Driver Configuration macros */ +#define VOUT_NAME "omap_vout" + +enum omap_vout_channels { + OMAP_VIDEO1, + OMAP_VIDEO2, +}; + +enum dma_channel_state { + DMA_CHAN_NOT_ALLOTED, + DMA_CHAN_ALLOTED, +}; + +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 + +/* Max Resolution supported by the driver */ +#define VID_MAX_WIDTH 1280 /* Largest width */ +#define VID_MAX_HEIGHT 720 /* Largest height */ + +/* Mimimum requirement is 2x2 for DSS */ +#define VID_MIN_WIDTH 2 +#define VID_MIN_HEIGHT 2 + +/* 2048 x 2048 is max res supported by OMAP display controller */ +#define MAX_PIXELS_PER_LINE 2048 + +#define VRFB_TX_TIMEOUT 1000 +#define VRFB_NUM_BUFS 4 + +/* Max buffer size tobe allocated during init */ +#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4) + +static struct videobuf_queue_ops video_vbq_ops; +/* Variables configurable through module params*/ +static u32 video1_numbuffers = 3; +static u32 video2_numbuffers = 3; +static u32 video1_bufsize = OMAP_VOUT_MAX_BUF_SIZE; +static u32 video2_bufsize = OMAP_VOUT_MAX_BUF_SIZE; +static u32 vid1_static_vrfb_alloc; +static u32 vid2_static_vrfb_alloc; +static int debug; + +/* Module parameters */ +module_param(video1_numbuffers, uint, S_IRUGO); +MODULE_PARM_DESC(video1_numbuffers, + "Number of buffers to be allocated at init time for Video1 device."); + +module_param(video2_numbuffers, uint, S_IRUGO); +MODULE_PARM_DESC(video2_numbuffers, + "Number of buffers to be allocated at init time for Video2 device."); + +module_param(video1_bufsize, uint, S_IRUGO); +MODULE_PARM_DESC(video1_bufsize, + "Size of the buffer to be allocated for video1 device"); + +module_param(video2_bufsize, uint, S_IRUGO); +MODULE_PARM_DESC(video2_bufsize, + "Size of the buffer to be allocated for video2 device"); + +module_param(vid1_static_vrfb_alloc, bool, S_IRUGO); +MODULE_PARM_DESC(vid1_static_vrfb_alloc, + "Static allocation of the VRFB buffer for video1 device"); + +module_param(vid2_static_vrfb_alloc, bool, S_IRUGO); +MODULE_PARM_DESC(vid2_static_vrfb_alloc, + "Static allocation of the VRFB buffer for video2 device"); + +module_param(debug, bool, S_IRUGO); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* list of image formats supported by OMAP2 video pipelines */ +const static struct v4l2_fmtdesc omap_formats[] = { + { + /* Note: V4L2 defines RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 + * + * We interpret RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 + */ + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + { + /* Note: V4L2 defines RGB32 as: RGB-8-8-8-8 we use + * this for RGB24 unpack mode, the last 8 bits are ignored + * */ + .description = "RGB32, le", + .pixelformat = V4L2_PIX_FMT_RGB32, + }, + { + /* Note: V4L2 defines RGB24 as: RGB-8-8-8 we use + * this for RGB24 packed mode + * + */ + .description = "RGB24, le", + .pixelformat = V4L2_PIX_FMT_RGB24, + }, + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + { + .description = "UYVY, packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, +}; + +#define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats)) + +/* + * Allocate buffers + */ +static unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr) +{ + u32 order, size; + unsigned long virt_addr, addr; + + size = PAGE_ALIGN(buf_size); + order = get_order(size); + virt_addr = __get_free_pages(GFP_KERNEL | GFP_DMA, order); + addr = virt_addr; + + if (virt_addr) { + while (size > 0) { + SetPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + } + *phys_addr = (u32) virt_to_phys((void *) virt_addr); + return virt_addr; +} + +/* + * Free buffers + */ +static void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size) +{ + u32 order, size; + unsigned long addr = virtaddr; + + size = PAGE_ALIGN(buf_size); + order = get_order(size); + + while (size > 0) { + ClearPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + free_pages((unsigned long) virtaddr, order); +} + +/* + * Function for allocating video buffers + */ +static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout, + unsigned int *count, int startindex) +{ + int i, j; + + for (i = 0; i < *count; i++) { + if (!vout->smsshado_virt_addr[i]) { + vout->smsshado_virt_addr[i] = + omap_vout_alloc_buffer(vout->smsshado_size, + &vout->smsshado_phy_addr[i]); + } + if (!vout->smsshado_virt_addr[i] && startindex != -1) { + if (V4L2_MEMORY_MMAP == vout->memory && i >= startindex) + break; + } + if (!vout->smsshado_virt_addr[i]) { + for (j = 0; j < i; j++) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } + *count = 0; + return -ENOMEM; + } + memset((void *) vout->smsshado_virt_addr[i], 0, + vout->smsshado_size); + } + return 0; +} + +/* + * Try format + */ +static int omap_vout_try_format(struct v4l2_pix_format *pix) +{ + int ifmt, bpp = 0; + + pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT, + (u32)VID_MAX_HEIGHT); + pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH); + + for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) { + if (pix->pixelformat == omap_formats[ifmt].pixelformat) + break; + } + + if (ifmt == NUM_OUTPUT_FORMATS) + ifmt = 0; + + pix->pixelformat = omap_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_ANY; + pix->priv = 0; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + bpp = YUYV_BPP; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB565_BPP; + break; + case V4L2_PIX_FMT_RGB24: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB24_BPP; + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB32_BPP; + break; + } + pix->bytesperline = pix->width * bpp; + pix->sizeimage = pix->bytesperline * pix->height; + + return bpp; +} + +/* + * omap_vout_uservirt_to_phys: This inline function is used to convert user + * space virtual address to physical address. + */ +static u32 omap_vout_uservirt_to_phys(u32 virtp) +{ + unsigned long physp = 0; + struct vm_area_struct *vma; + struct mm_struct *mm = current->mm; + + vma = find_vma(mm, virtp); + /* For kernel direct-mapped memory, take the easy way */ + if (virtp >= PAGE_OFFSET) { + physp = virt_to_phys((void *) virtp); + } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { + /* this will catch, kernel-allocated, mmaped-to-usermode + addresses */ + physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + } else { + /* otherwise, use get_user_pages() for general userland pages */ + int res, nr_pages = 1; + struct page *pages; + down_read(¤t->mm->mmap_sem); + + res = get_user_pages(current, current->mm, virtp, nr_pages, 1, + 0, &pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (res == nr_pages) { + physp = __pa(page_address(&pages[0]) + + (virtp & ~PAGE_MASK)); + } else { + printk(KERN_WARNING VOUT_NAME + "get_user_pages failed\n"); + return 0; + } + } + + return physp; +} + +/* + * Wakes up the application once the DMA transfer to VRFB space is completed. + */ +static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data) +{ + struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data; + + t->tx_status = 1; + wake_up_interruptible(&t->wait); +} + +/* + * Release the VRFB context once the module exits + */ +static void omap_vout_release_vrfb(struct omap_vout_device *vout) +{ + int i; + + for (i = 0; i < VRFB_NUM_BUFS; i++) + omap_vrfb_release_ctx(&vout->vrfb_context[i]); + + if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) { + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + omap_free_dma(vout->vrfb_dma_tx.dma_ch); + } +} + +/* + * Return true if rotation is 90 or 270 + */ +static inline int rotate_90_or_270(const struct omap_vout_device *vout) +{ + return (vout->rotation == dss_rotation_90_degree || + vout->rotation == dss_rotation_270_degree); +} + +/* + * Return true if rotation is enabled + */ +static inline int rotation_enabled(const struct omap_vout_device *vout) +{ + return vout->rotation || vout->mirror; +} + +/* + * Reverse the rotation degree if mirroring is enabled + */ +static inline int calc_rotation(const struct omap_vout_device *vout) +{ + if (!vout->mirror) + return vout->rotation; + + switch (vout->rotation) { + case dss_rotation_90_degree: + return dss_rotation_270_degree; + case dss_rotation_270_degree: + return dss_rotation_90_degree; + case dss_rotation_180_degree: + return dss_rotation_0_degree; + default: + return dss_rotation_180_degree; + } +} + +/* + * Free the V4L2 buffers + */ +static void omap_vout_free_buffers(struct omap_vout_device *vout) +{ + int i, numbuffers; + + /* Allocate memory for the buffers */ + numbuffers = (vout->vid) ? video2_numbuffers : video1_numbuffers; + vout->buffer_size = (vout->vid) ? video2_bufsize : video1_bufsize; + + for (i = 0; i < numbuffers; i++) { + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buffer_size); + vout->buf_phy_addr[i] = 0; + vout->buf_virt_addr[i] = 0; + } +} + +/* + * Free VRFB buffers + */ +static void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) +{ + int j; + + for (j = 0; j < VRFB_NUM_BUFS; j++) { + omap_vout_free_buffer(vout->smsshado_virt_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } +} + +/* + * Allocate the buffers for the VRFB space. Data is copied from V4L2 + * buffers to the VRFB buffers using the DMA engine. + */ +static int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, + unsigned int *count, unsigned int startindex) +{ + int i; + bool yuv_mode; + + /* Allocate the VRFB buffers only if the buffers are not + * allocated during init time. + */ + if ((rotation_enabled(vout)) && !vout->vrfb_static_allocation) + if (omap_vout_allocate_vrfb_buffers(vout, count, startindex)) + return -ENOMEM; + + if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 || + vout->dss_mode == OMAP_DSS_COLOR_UYVY) + yuv_mode = true; + else + yuv_mode = false; + + for (i = 0; i < *count; i++) + omap_vrfb_setup(&vout->vrfb_context[i], + vout->smsshado_phy_addr[i], vout->pix.width, + vout->pix.height, vout->bpp, yuv_mode); + + return 0; +} + +/* + * Convert V4L2 rotation to DSS rotation + * V4L2 understand 0, 90, 180, 270. + * Convert to 0, 1, 2 and 3 repsectively for DSS + */ +static int v4l2_rot_to_dss_rot(int v4l2_rotation, + enum dss_rotation *rotation, bool mirror) +{ + int ret = 0; + + switch (v4l2_rotation) { + case 90: + *rotation = dss_rotation_90_degree; + break; + case 180: + *rotation = dss_rotation_180_degree; + break; + case 270: + *rotation = dss_rotation_270_degree; + break; + case 0: + *rotation = dss_rotation_0_degree; + break; + default: + ret = -EINVAL; + } + return ret; +} + +/* + * Calculate the buffer offsets from which the streaming should + * start. This offset calculation is mainly required because of + * the VRFB 32 pixels alignment with rotation. + */ +static int omap_vout_calculate_offset(struct omap_vout_device *vout) +{ + struct omap_overlay *ovl; + enum dss_rotation rotation; + struct omapvideo_info *ovid; + bool mirroring = vout->mirror; + struct omap_dss_device *cur_display; + struct v4l2_rect *crop = &vout->crop; + struct v4l2_pix_format *pix = &vout->pix; + int *cropped_offset = &vout->cropped_offset; + int vr_ps = 1, ps = 2, temp_ps = 2; + int offset = 0, ctop = 0, cleft = 0, line_length = 0; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) + return -1; + + cur_display = ovl->manager->device; + rotation = calc_rotation(vout); + + if (V4L2_PIX_FMT_YUYV == pix->pixelformat || + V4L2_PIX_FMT_UYVY == pix->pixelformat) { + if (rotation_enabled(vout)) { + /* + * ps - Actual pixel size for YUYV/UYVY for + * VRFB/Mirroring is 4 bytes + * vr_ps - Virtually pixel size for YUYV/UYVY is + * 2 bytes + */ + ps = 4; + vr_ps = 2; + } else { + ps = 2; /* otherwise the pixel size is 2 byte */ + } + } else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) { + ps = 4; + } else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) { + ps = 3; + } + vout->ps = ps; + vout->vr_ps = vr_ps; + + if (rotation_enabled(vout)) { + line_length = MAX_PIXELS_PER_LINE; + ctop = (pix->height - crop->height) - crop->top; + cleft = (pix->width - crop->width) - crop->left; + } else { + line_length = pix->width; + } + vout->line_length = line_length; + switch (rotation) { + case dss_rotation_90_degree: + offset = vout->vrfb_context[0].yoffset * + vout->vrfb_context[0].bytespp; + temp_ps = ps / vr_ps; + if (mirroring == 0) { + *cropped_offset = offset + line_length * + temp_ps * cleft + crop->top * temp_ps; + } else { + *cropped_offset = offset + line_length * temp_ps * + cleft + crop->top * temp_ps + (line_length * + ((crop->width / (vr_ps)) - 1) * ps); + } + break; + case dss_rotation_180_degree: + offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset * + vout->vrfb_context[0].bytespp) + + (vout->vrfb_context[0].xoffset * + vout->vrfb_context[0].bytespp)); + if (mirroring == 0) { + *cropped_offset = offset + (line_length * ps * ctop) + + (cleft / vr_ps) * ps; + + } else { + *cropped_offset = offset + (line_length * ps * ctop) + + (cleft / vr_ps) * ps + (line_length * + (crop->height - 1) * ps); + } + break; + case dss_rotation_270_degree: + offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset * + vout->vrfb_context[0].bytespp; + temp_ps = ps / vr_ps; + if (mirroring == 0) { + *cropped_offset = offset + line_length * + temp_ps * crop->left + ctop * ps; + } else { + *cropped_offset = offset + line_length * + temp_ps * crop->left + ctop * ps + + (line_length * ((crop->width / vr_ps) - 1) * + ps); + } + break; + case dss_rotation_0_degree: + if (mirroring == 0) { + *cropped_offset = (line_length * ps) * + crop->top + (crop->left / vr_ps) * ps; + } else { + *cropped_offset = (line_length * ps) * + crop->top + (crop->left / vr_ps) * ps + + (line_length * (crop->height - 1) * ps); + } + break; + default: + *cropped_offset = (line_length * ps * crop->top) / + vr_ps + (crop->left * ps) / vr_ps + + ((crop->width / vr_ps) - 1) * ps; + break; + } + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n", + __func__, *cropped_offset); + return 0; +} + +/* + * Convert V4L2 pixel format to DSS pixel format + */ +static int video_mode_to_dss_mode(struct omap_vout_device *vout) +{ + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct v4l2_pix_format *pix = &vout->pix; + enum omap_color_mode mode; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + switch (pix->pixelformat) { + case 0: + break; + case V4L2_PIX_FMT_YUYV: + mode = OMAP_DSS_COLOR_YUV2; + break; + case V4L2_PIX_FMT_UYVY: + mode = OMAP_DSS_COLOR_UYVY; + break; + case V4L2_PIX_FMT_RGB565: + mode = OMAP_DSS_COLOR_RGB16; + break; + case V4L2_PIX_FMT_RGB24: + mode = OMAP_DSS_COLOR_RGB24P; + break; + case V4L2_PIX_FMT_RGB32: + mode = (ovl->id == OMAP_DSS_VIDEO1) ? + OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32; + break; + case V4L2_PIX_FMT_BGR32: + mode = OMAP_DSS_COLOR_RGBX32; + break; + default: + mode = -EINVAL; + } + return mode; +} + +/* + * Setup the overlay + */ +int omapvid_setup_overlay(struct omap_vout_device *vout, + struct omap_overlay *ovl, int posx, int posy, int outw, + int outh, u32 addr) +{ + int ret = 0; + struct omap_overlay_info info; + int cropheight, cropwidth, pixheight, pixwidth; + + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 && + (outw != vout->pix.width || outh != vout->pix.height)) { + ret = -EINVAL; + goto setup_ovl_err; + } + + vout->dss_mode = video_mode_to_dss_mode(vout); + if (vout->dss_mode == -EINVAL) { + ret = -EINVAL; + goto setup_ovl_err; + } + + /* Setup the input plane parameters according to + * rotation value selected. + */ + if (rotate_90_or_270(vout)) { + cropheight = vout->crop.width; + cropwidth = vout->crop.height; + pixheight = vout->pix.width; + pixwidth = vout->pix.height; + } else { + cropheight = vout->crop.height; + cropwidth = vout->crop.width; + pixheight = vout->pix.height; + pixwidth = vout->pix.width; + } + + ovl->get_overlay_info(ovl, &info); + info.paddr = addr; + info.vaddr = NULL; + info.width = cropwidth; + info.height = cropheight; + info.color_mode = vout->dss_mode; + info.mirror = vout->mirror; + info.pos_x = posx; + info.pos_y = posy; + info.out_width = outw; + info.out_height = outh; + info.global_alpha = vout->win.global_alpha; + if (!rotation_enabled(vout)) { + info.rotation = 0; + info.rotation_type = OMAP_DSS_ROT_DMA; + info.screen_width = pixwidth; + } else { + info.rotation = vout->rotation; + info.rotation_type = OMAP_DSS_ROT_VRFB; + info.screen_width = 2048; + } + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n" + "rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n" + "out_height=%d rotation_type=%d screen_width=%d\n", + __func__, info.enabled, info.paddr, info.width, info.height, + info.color_mode, info.rotation, info.mirror, info.pos_x, + info.pos_y, info.out_width, info.out_height, info.rotation_type, + info.screen_width); + + ret = ovl->set_overlay_info(ovl, &info); + if (ret) + goto setup_ovl_err; + + return 0; + +setup_ovl_err: + v4l2_warn(&vout->vid_dev->v4l2_dev, "setup_overlay failed\n"); + return ret; +} + +/* + * Initialize the overlay structure + */ +int omapvid_init(struct omap_vout_device *vout, u32 addr) +{ + int ret = 0, i; + struct v4l2_window *win; + struct omap_overlay *ovl; + int posx, posy, outw, outh, temp; + struct omap_video_timings *timing; + struct omapvideo_info *ovid = &vout->vid_info; + + win = &vout->win; + for (i = 0; i < ovid->num_overlays; i++) { + ovl = ovid->overlays[i]; + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + + timing = &ovl->manager->device->panel.timings; + + outw = win->w.width; + outh = win->w.height; + switch (vout->rotation) { + case dss_rotation_90_degree: + /* Invert the height and width for 90 + * and 270 degree rotation + */ + temp = outw; + outw = outh; + outh = temp; + posy = (timing->y_res - win->w.width) - win->w.left; + posx = win->w.top; + break; + + case dss_rotation_180_degree: + posx = (timing->x_res - win->w.width) - win->w.left; + posy = (timing->y_res - win->w.height) - win->w.top; + break; + + case dss_rotation_270_degree: + temp = outw; + outw = outh; + outh = temp; + posy = win->w.left; + posx = (timing->x_res - win->w.height) - win->w.top; + break; + + default: + posx = win->w.left; + posy = win->w.top; + break; + } + + ret = omapvid_setup_overlay(vout, ovl, posx, posy, + outw, outh, addr); + if (ret) + goto omapvid_init_err; + } + return 0; + +omapvid_init_err: + v4l2_warn(&vout->vid_dev->v4l2_dev, "apply_changes failed\n"); + return ret; +} + +/* + * Apply the changes set the go bit of DSS + */ +int omapvid_apply_changes(struct omap_vout_device *vout) +{ + int i; + struct omap_overlay *ovl; + struct omapvideo_info *ovid = &vout->vid_info; + + for (i = 0; i < ovid->num_overlays; i++) { + ovl = ovid->overlays[i]; + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + ovl->manager->apply(ovl->manager); + } + + return 0; +} + +void omap_vout_isr(void *arg, unsigned int irqstatus) +{ + int ret; + u32 addr, fid; + struct omap_overlay *ovl; + struct timeval timevalue; + struct omapvideo_info *ovid; + struct omap_dss_device *cur_display; + struct omap_vout_device *vout = (struct omap_vout_device *)arg; + + if (!vout->streaming) + return; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) + return; + + cur_display = ovl->manager->device; + + spin_lock(&vout->vbq_lock); + do_gettimeofday(&timevalue); + if (cur_display->type == OMAP_DISPLAY_TYPE_DPI) { + if (!(irqstatus & DISPC_IRQ_VSYNC)) + goto vout_isr_err; + + if (!vout->first_int && (vout->cur_frm != vout->next_frm)) { + vout->cur_frm->ts = timevalue; + vout->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->cur_frm->done); + vout->cur_frm = vout->next_frm; + } + vout->first_int = 0; + if (list_empty(&vout->dma_queue)) + goto vout_isr_err; + + vout->next_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vout->next_frm->queue); + + vout->next_frm->state = VIDEOBUF_ACTIVE; + + addr = (unsigned long) vout->queued_buf_addr[vout->next_frm->i] + + vout->cropped_offset; + + /* First save the configuration in ovelray structure */ + ret = omapvid_init(vout, addr); + if (ret) + printk(KERN_ERR VOUT_NAME + "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + ret = omapvid_apply_changes(vout); + if (ret) + printk(KERN_ERR VOUT_NAME "failed to change mode\n"); + } else { + + if (vout->first_int) { + vout->first_int = 0; + goto vout_isr_err; + } + if (irqstatus & DISPC_IRQ_EVSYNC_ODD) + fid = 1; + else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN) + fid = 0; + else + goto vout_isr_err; + + vout->field_id ^= 1; + if (fid != vout->field_id) { + if (0 == fid) + vout->field_id = fid; + + goto vout_isr_err; + } + if (0 == fid) { + if (vout->cur_frm == vout->next_frm) + goto vout_isr_err; + + vout->cur_frm->ts = timevalue; + vout->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->cur_frm->done); + vout->cur_frm = vout->next_frm; + } else if (1 == fid) { + if (list_empty(&vout->dma_queue) || + (vout->cur_frm != vout->next_frm)) + goto vout_isr_err; + + vout->next_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vout->next_frm->queue); + + vout->next_frm->state = VIDEOBUF_ACTIVE; + addr = (unsigned long) + vout->queued_buf_addr[vout->next_frm->i] + + vout->cropped_offset; + /* First save the configuration in ovelray structure */ + ret = omapvid_init(vout, addr); + if (ret) + printk(KERN_ERR VOUT_NAME + "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + ret = omapvid_apply_changes(vout); + if (ret) + printk(KERN_ERR VOUT_NAME + "failed to change mode\n"); + } + + } + +vout_isr_err: + spin_unlock(&vout->vbq_lock); +} + + +/* Video buffer call backs */ + +/* + * Buffer setup function is called by videobuf layer when REQBUF ioctl is + * called. This is used to setup buffers and return size and count of + * buffers allocated. After the call to this buffer, videobuf layer will + * setup buffer queue depending on the size and count of buffers + */ +static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + int startindex = 0, i, j; + u32 phy_addr = 0, virt_addr = 0; + struct omap_vout_device *vout = q->priv_data; + + if (!vout) + return -EINVAL; + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) + return -EINVAL; + + startindex = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex) + *count = startindex; + + if ((rotation_enabled(vout)) && *count > VRFB_NUM_BUFS) + *count = VRFB_NUM_BUFS; + + /* If rotation is enabled, allocate memory for VRFB space also */ + if (rotation_enabled(vout)) + if (omap_vout_vrfb_buffer_setup(vout, count, startindex)) + return -ENOMEM; + + if (V4L2_MEMORY_MMAP != vout->memory) + return 0; + + /* Now allocated the V4L2 buffers */ + *size = PAGE_ALIGN(vout->pix.width * vout->pix.height * vout->bpp); + startindex = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + + for (i = startindex; i < *count; i++) { + vout->buffer_size = *size; + + virt_addr = omap_vout_alloc_buffer(vout->buffer_size, + &phy_addr); + if (!virt_addr) { + if (!rotation_enabled(vout)) + break; + /* Free the VRFB buffers if no space for V4L2 buffers */ + for (j = i; j < *count; j++) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[j], + vout->smsshado_size); + vout->smsshado_virt_addr[j] = 0; + vout->smsshado_phy_addr[j] = 0; + } + } + vout->buf_virt_addr[i] = virt_addr; + vout->buf_phy_addr[i] = phy_addr; + } + *count = vout->buffer_allocated = i; + + return 0; +} + +/* + * Free the V4L2 buffers additionally allocated than default + * number of buffers and free all the VRFB buffers + */ +static void omap_vout_free_allbuffers(struct omap_vout_device *vout) +{ + int num_buffers = 0, i; + + num_buffers = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + + for (i = num_buffers; i < vout->buffer_allocated; i++) { + if (vout->buf_virt_addr[i]) + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buffer_size); + + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + /* Free the VRFB buffers only if they are allocated + * during reqbufs. Don't free if init time allocated + */ + if (!vout->vrfb_static_allocation) { + for (i = 0; i < VRFB_NUM_BUFS; i++) { + if (vout->smsshado_virt_addr[i]) { + omap_vout_free_buffer( + vout->smsshado_virt_addr[i], + vout->smsshado_size); + vout->smsshado_virt_addr[i] = 0; + vout->smsshado_phy_addr[i] = 0; + } + } + } + vout->buffer_allocated = num_buffers; +} + +/* + * This function will be called when VIDIOC_QBUF ioctl is called. + * It prepare buffers before give out for the display. This function + * converts user space virtual address into physical address if userptr memory + * exchange mechanism is used. If rotation is enabled, it copies entire + * buffer into VRFB memory space before giving it to the DSS. + */ +static int omap_vout_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vid_vrfb_dma *tx; + enum dss_rotation rotation; + struct videobuf_dmabuf *dmabuf = NULL; + struct omap_vout_device *vout = q->priv_data; + u32 dest_frame_index = 0, src_element_index = 0; + u32 dest_element_index = 0, src_frame_index = 0; + u32 elem_count = 0, frame_count = 0, pixsize = 2; + + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vout->pix.width; + vb->height = vout->pix.height; + vb->size = vb->width * vb->height * vout->bpp; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + /* if user pointer memory mechanism is used, get the physical + * address of the buffer + */ + if (V4L2_MEMORY_USERPTR == vb->memory) { + if (0 == vb->baddr) + return -EINVAL; + /* Virtual address */ + /* priv points to struct videobuf_pci_sg_memory. But we went + * pointer to videobuf_dmabuf, which is member of + * videobuf_pci_sg_memory */ + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + dmabuf->vmalloc = (void *) vb->baddr; + + /* Physical address */ + dmabuf->bus_addr = + (dma_addr_t) omap_vout_uservirt_to_phys(vb->baddr); + } + + if (!rotation_enabled(vout)) { + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + vout->queued_buf_addr[vb->i] = (u8 *) dmabuf->bus_addr; + return 0; + } + dmabuf = videobuf_to_dma(q->bufs[vb->i]); + /* If rotation is enabled, copy input buffer into VRFB + * memory space using DMA. We are copying input buffer + * into VRFB memory space of desired angle and DSS will + * read image VRFB memory for 0 degree angle + */ + pixsize = vout->bpp * vout->vrfb_bpp; + /* + * DMA transfer in double index mode + */ + + /* Frame index */ + dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) - + (vout->pix.width * vout->bpp)) + 1; + + /* Source and destination parameters */ + src_element_index = 0; + src_frame_index = 0; + dest_element_index = 1; + /* Number of elements per frame */ + elem_count = vout->pix.width * vout->bpp; + frame_count = vout->pix.height; + tx = &vout->vrfb_dma_tx; + tx->tx_status = 0; + omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32, + (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT, + tx->dev_id, 0x0); + /* src_port required only for OMAP1 */ + omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + dmabuf->bus_addr, src_element_index, src_frame_index); + /*set dma source burst mode for VRFB */ + omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); + rotation = calc_rotation(vout); + + /* dest_port required only for OMAP1 */ + omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX, + vout->vrfb_context[vb->i].paddr[0], dest_element_index, + dest_frame_index); + /*set dma dest burst mode for VRFB */ + omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); + omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0); + + omap_start_dma(tx->dma_ch); + interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT); + + if (tx->tx_status == 0) { + omap_stop_dma(tx->dma_ch); + return -EINVAL; + } + /* Store buffers physical address into an array. Addresses + * from this array will be used to configure DSS */ + vout->queued_buf_addr[vb->i] = (u8 *) + vout->vrfb_context[vb->i].paddr[rotation]; + return 0; +} + +/* + * Buffer queue funtion will be called from the videobuf layer when _QBUF + * ioctl is called. It is used to enqueue buffer, which is ready to be + * displayed. + */ +static void omap_vout_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct omap_vout_device *vout = q->priv_data; + + /* Driver is also maintainig a queue. So enqueue buffer in the driver + * queue */ + list_add_tail(&vb->queue, &vout->dma_queue); + + vb->state = VIDEOBUF_QUEUED; +} + +/* + * Buffer release function is called from videobuf layer to release buffer + * which are already allocated + */ +static void omap_vout_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct omap_vout_device *vout = q->priv_data; + + vb->state = VIDEOBUF_NEEDS_INIT; + + if (V4L2_MEMORY_MMAP != vout->memory) + return; +} + +/* + * File operations + */ +static void omap_vout_vm_open(struct vm_area_struct *vma) +{ + struct omap_vout_device *vout = vma->vm_private_data; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end); + vout->mmap_count++; +} + +static void omap_vout_vm_close(struct vm_area_struct *vma) +{ + struct omap_vout_device *vout = vma->vm_private_data; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end); + vout->mmap_count--; +} + +static struct vm_operations_struct omap_vout_vm_ops = { + .open = omap_vout_vm_open, + .close = omap_vout_vm_close, +}; + +static int omap_vout_mmap(struct file *file, struct vm_area_struct *vma) +{ + int i; + void *pos; + unsigned long start = vma->vm_start; + unsigned long size = (vma->vm_end - vma->vm_start); + struct videobuf_dmabuf *dmabuf = NULL; + struct omap_vout_device *vout = file->private_data; + struct videobuf_queue *q = &vout->vbq; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + " %s pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__, + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + /* look for the buffer to map */ + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + if (V4L2_MEMORY_MMAP != q->bufs[i]->memory) + continue; + if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT)) + break; + } + + if (VIDEO_MAX_FRAME == i) { + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, + "offset invalid [offset=0x%lx]\n", + (vma->vm_pgoff << PAGE_SHIFT)); + return -EINVAL; + } + q->bufs[i]->baddr = vma->vm_start; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &omap_vout_vm_ops; + vma->vm_private_data = (void *) vout; + dmabuf = videobuf_to_dma(q->bufs[i]); + pos = dmabuf->vmalloc; + vma->vm_pgoff = virt_to_phys((void *)pos) >> PAGE_SHIFT; + while (size > 0) { + unsigned long pfn; + pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT; + if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + vout->mmap_count++; + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + + return 0; +} + +static int omap_vout_release(struct file *file) +{ + unsigned int ret, i; + struct videobuf_queue *q; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = file->private_data; + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__); + ovid = &vout->vid_info; + + if (!vout) + return 0; + + q = &vout->vbq; + /* Disable all the overlay managers connected with this interface */ + for (i = 0; i < ovid->num_overlays; i++) { + struct omap_overlay *ovl = ovid->overlays[i]; + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + ovl->get_overlay_info(ovl, &info); + info.enabled = 0; + ovl->set_overlay_info(ovl, &info); + } + } + /* Turn off the pipeline */ + ret = omapvid_apply_changes(vout); + if (ret) + v4l2_warn(&vout->vid_dev->v4l2_dev, + "Unable to apply changes\n"); + + /* Free all buffers */ + omap_vout_free_allbuffers(vout); + videobuf_mmap_free(q); + + /* Even if apply changes fails we should continue + freeing allocated memeory */ + if (vout->streaming) { + u32 mask = 0; + + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | + DISPC_IRQ_EVSYNC_ODD; + omap_dispc_unregister_isr(omap_vout_isr, vout, mask); + vout->streaming = 0; + + videobuf_streamoff(q); + videobuf_queue_cancel(q); + } + + if (vout->mmap_count != 0) + vout->mmap_count = 0; + + vout->opened -= 1; + file->private_data = NULL; + + if (vout->buffer_allocated) + videobuf_mmap_free(q); + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + return ret; +} + +static int omap_vout_open(struct file *file) +{ + struct videobuf_queue *q; + struct omap_vout_device *vout = NULL; + + vout = video_drvdata(file); + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__); + + if (vout == NULL) + return -ENODEV; + + /* for now, we only support single open */ + if (vout->opened) + return -EBUSY; + + vout->opened += 1; + + file->private_data = vout; + vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + q = &vout->vbq; + video_vbq_ops.buf_setup = omap_vout_buffer_setup; + video_vbq_ops.buf_prepare = omap_vout_buffer_prepare; + video_vbq_ops.buf_release = omap_vout_buffer_release; + video_vbq_ops.buf_queue = omap_vout_buffer_queue; + spin_lock_init(&vout->vbq_lock); + + videobuf_queue_sg_init(q, &video_vbq_ops, NULL, &vout->vbq_lock, + vout->type, V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), vout); + + v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__); + return 0; +} + +/* + * V4L2 ioctls + */ +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct omap_vout_device *vout = fh; + + strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); + strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); + cap->bus_info[0] = '\0'; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; + + return 0; +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + fmt->index = index; + fmt->type = type; + if (index >= NUM_OUTPUT_FORMATS) + return -EINVAL; + + fmt->flags = omap_formats[index].flags; + strlcpy(fmt->description, omap_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = omap_formats[index].pixelformat; + + return 0; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_vout_device *vout = fh; + + f->fmt.pix = vout->pix; + return 0; + +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_video_timings *timing; + struct omap_vout_device *vout = fh; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + if (!ovl->manager || !ovl->manager->device) + return -EINVAL; + /* get the display device attached to the overlay */ + timing = &ovl->manager->device->panel.timings; + + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + + omap_vout_try_format(&f->fmt.pix); + return 0; +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + int ret, bpp; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_video_timings *timing; + struct omap_vout_device *vout = fh; + + if (vout->streaming) + return -EBUSY; + + mutex_lock(&vout->lock); + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + /* get the display device attached to the overlay */ + if (!ovl->manager || !ovl->manager->device) { + ret = -EINVAL; + goto s_fmt_vid_out_exit; + } + timing = &ovl->manager->device->panel.timings; + + /* We dont support RGB24-packed mode if vrfb rotation + * is enabled*/ + if ((rotation_enabled(vout)) && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) { + ret = -EINVAL; + goto s_fmt_vid_out_exit; + } + + /* get the framebuffer parameters */ + + if (rotate_90_or_270(vout)) { + vout->fbuf.fmt.height = timing->x_res; + vout->fbuf.fmt.width = timing->y_res; + } else { + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + } + + /* change to samller size is OK */ + + bpp = omap_vout_try_format(&f->fmt.pix); + f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp; + + /* try & set the new output format */ + vout->bpp = bpp; + vout->pix = f->fmt.pix; + vout->vrfb_bpp = 1; + + /* If YUYV then vrfb bpp is 2, for others its 1 */ + if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat || + V4L2_PIX_FMT_UYVY == vout->pix.pixelformat) + vout->vrfb_bpp = 2; + + /* set default crop and win */ + omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win); + + /* Save the changes in the overlay strcuture */ + ret = omapvid_init(vout, 0); + if (ret) { + v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n"); + goto s_fmt_vid_out_exit; + } + + ret = 0; + +s_fmt_vid_out_exit: + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + int ret = 0; + struct omap_vout_device *vout = fh; + struct v4l2_window *win = &f->fmt.win; + + ret = omap_vout_try_window(&vout->fbuf, win); + + if (!ret) { + if (vout->vid == OMAP_VIDEO1) + win->global_alpha = 255; + else + win->global_alpha = f->fmt.win.global_alpha; + } + + return ret; +} + +static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + int ret = 0; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = fh; + struct v4l2_window *win = &f->fmt.win; + + mutex_lock(&vout->lock); + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win); + if (!ret) { + /* Video1 plane does not support global alpha */ + if (ovl->id == OMAP_DSS_VIDEO1) + vout->win.global_alpha = 255; + else + vout->win.global_alpha = f->fmt.win.global_alpha; + + vout->win.chromakey = f->fmt.win.chromakey; + } + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + fmt->index = index; + fmt->type = type; + if (index >= NUM_OUTPUT_FORMATS) + return -EINVAL; + + fmt->flags = omap_formats[index].flags; + strlcpy(fmt->description, omap_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = omap_formats[index].pixelformat; + return 0; +} + +static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + u32 key_value = 0; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = fh; + struct omap_overlay_manager_info info; + struct v4l2_window *win = &f->fmt.win; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + win->w = vout->win.w; + win->field = vout->win.field; + win->global_alpha = vout->win.global_alpha; + + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + key_value = info.trans_key; + } + win->chromakey = key_value; + return 0; +} + +static int vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + struct omap_vout_device *vout = fh; + struct v4l2_pix_format *pix = &vout->pix; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + /* Width and height are always even */ + cropcap->bounds.width = pix->width & ~1; + cropcap->bounds.height = pix->height & ~1; + + omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect); + cropcap->pixelaspect.numerator = 1; + cropcap->pixelaspect.denominator = 1; + return 0; +} + +static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct omap_vout_device *vout = fh; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + crop->c = vout->crop; + return 0; +} + +static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + int ret = -EINVAL; + struct omap_vout_device *vout = fh; + struct omapvideo_info *ovid; + struct omap_overlay *ovl; + struct omap_video_timings *timing; + + if (vout->streaming) + return -EBUSY; + + mutex_lock(&vout->lock); + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + if (!ovl->manager || !ovl->manager->device) { + ret = -EINVAL; + goto s_crop_err; + } + /* get the display device attached to the overlay */ + timing = &ovl->manager->device->panel.timings; + + if (rotate_90_or_270(vout)) { + vout->fbuf.fmt.height = timing->x_res; + vout->fbuf.fmt.width = timing->y_res; + } else { + vout->fbuf.fmt.height = timing->y_res; + vout->fbuf.fmt.width = timing->x_res; + } + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win, + &vout->fbuf, &crop->c); + +s_crop_err: + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *ctrl) +{ + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0); + break; + case V4L2_CID_BG_COLOR: + ret = v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0); + break; + case V4L2_CID_VFLIP: + ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); + break; + default: + ctrl->name[0] = '\0'; + ret = -EINVAL; + } + return ret; +} + +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) +{ + int ret = 0; + struct omap_vout_device *vout = fh; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ctrl->value = vout->control[0].value; + break; + case V4L2_CID_BG_COLOR: + { + struct omap_overlay_manager_info info; + struct omap_overlay *ovl; + + ovl = vout->vid_info.overlays[0]; + if (!ovl->manager || !ovl->manager->get_manager_info) { + ret = -EINVAL; + break; + } + + ovl->manager->get_manager_info(ovl->manager, &info); + ctrl->value = info.default_color; + break; + } + case V4L2_CID_VFLIP: + ctrl->value = vout->control[2].value; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) +{ + int ret = 0; + struct omap_vout_device *vout = fh; + + switch (a->id) { + case V4L2_CID_ROTATE: + { + int rotation = a->value; + + mutex_lock(&vout->lock); + + if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + + if (v4l2_rot_to_dss_rot(rotation, &vout->rotation, + vout->mirror)) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + + vout->control[0].value = rotation; + mutex_unlock(&vout->lock); + break; + } + case V4L2_CID_BG_COLOR: + { + struct omap_overlay *ovl; + unsigned int color = a->value; + struct omap_overlay_manager_info info; + + ovl = vout->vid_info.overlays[0]; + + mutex_lock(&vout->lock); + if (!ovl->manager || !ovl->manager->get_manager_info) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + + ovl->manager->get_manager_info(ovl->manager, &info); + info.default_color = color; + if (ovl->manager->set_manager_info(ovl->manager, &info)) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + + vout->control[1].value = color; + mutex_unlock(&vout->lock); + break; + } + case V4L2_CID_VFLIP: + { + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + unsigned int mirror = a->value; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + mutex_lock(&vout->lock); + + if (mirror && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) { + mutex_unlock(&vout->lock); + ret = -EINVAL; + break; + } + vout->mirror = mirror; + vout->control[2].value = mirror; + mutex_unlock(&vout->lock); + break; + } + default: + ret = -EINVAL; + } + return ret; +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + int ret = 0; + unsigned int i, num_buffers = 0; + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + struct videobuf_dmabuf *dmabuf = NULL; + + if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0)) + return -EINVAL; + /* if memory is not mmp or userptr + return error */ + if ((V4L2_MEMORY_MMAP != req->memory) && + (V4L2_MEMORY_USERPTR != req->memory)) + return -EINVAL; + + mutex_lock(&vout->lock); + /* Cannot be requested when streaming is on */ + if (vout->streaming) { + ret = -EBUSY; + goto reqbuf_err; + } + + /* If buffers are already allocated free them */ + if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) { + if (vout->mmap_count) { + ret = -EBUSY; + goto reqbuf_err; + } + num_buffers = (vout->vid == OMAP_VIDEO1) ? + video1_numbuffers : video2_numbuffers; + for (i = num_buffers; i < vout->buffer_allocated; i++) { + dmabuf = videobuf_to_dma(q->bufs[i]); + omap_vout_free_buffer((u32)dmabuf->vmalloc, + vout->buffer_size); + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + vout->buffer_allocated = num_buffers; + videobuf_mmap_free(q); + } else if (q->bufs[0] && (V4L2_MEMORY_USERPTR == q->bufs[0]->memory)) { + if (vout->buffer_allocated) { + videobuf_mmap_free(q); + for (i = 0; i < vout->buffer_allocated; i++) { + kfree(q->bufs[i]); + q->bufs[i] = NULL; + } + vout->buffer_allocated = 0; + } + } + + /*store the memory type in data structure */ + vout->memory = req->memory; + + INIT_LIST_HEAD(&vout->dma_queue); + + /* call videobuf_reqbufs api */ + ret = videobuf_reqbufs(q, req); + if (ret < 0) + goto reqbuf_err; + + vout->buffer_allocated = req->count; + for (i = 0; i < req->count; i++) { + dmabuf = videobuf_to_dma(q->bufs[i]); + dmabuf->vmalloc = (void *) vout->buf_virt_addr[i]; + dmabuf->bus_addr = (dma_addr_t) vout->buf_phy_addr[i]; + dmabuf->sglen = 1; + } +reqbuf_err: + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + struct omap_vout_device *vout = fh; + + return videobuf_querybuf(&vout->vbq, b); +} + +static int vidioc_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buffer) +{ + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + + if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buffer->type) || + (buffer->index >= vout->buffer_allocated) || + (q->bufs[buffer->index]->memory != buffer->memory)) { + return -EINVAL; + } + if (V4L2_MEMORY_USERPTR == buffer->memory) { + if ((buffer->length < vout->pix.sizeimage) || + (0 == buffer->m.userptr)) { + return -EINVAL; + } + } + + if ((rotation_enabled(vout)) && + vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) { + v4l2_warn(&vout->vid_dev->v4l2_dev, + "DMA Channel not allocated for Rotation\n"); + return -EINVAL; + } + + return videobuf_qbuf(q, buffer); +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + + if (!vout->streaming) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) + /* Call videobuf_dqbuf for non blocking mode */ + return videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1); + else + /* Call videobuf_dqbuf for blocking mode */ + return videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0); +} + +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + int ret = 0, j; + u32 addr = 0, mask = 0; + struct omap_vout_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + struct omapvideo_info *ovid = &vout->vid_info; + + mutex_lock(&vout->lock); + + if (vout->streaming) { + ret = -EBUSY; + goto streamon_err; + } + + ret = videobuf_streamon(q); + if (ret) + goto streamon_err; + + if (list_empty(&vout->dma_queue)) { + ret = -EIO; + goto streamon_err1; + } + + /* Get the next frame from the buffer queue */ + vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove buffer from the buffer queue */ + list_del(&vout->cur_frm->queue); + /* Mark state of the current frame to active */ + vout->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vout->field_id = 0; + + /* set flag here. Next QBUF will start DMA */ + vout->streaming = 1; + + vout->first_int = 1; + + if (omap_vout_calculate_offset(vout)) { + ret = -EINVAL; + goto streamon_err1; + } + addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i] + + vout->cropped_offset; + + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD; + + omap_dispc_register_isr(omap_vout_isr, vout, mask); + + for (j = 0; j < ovid->num_overlays; j++) { + struct omap_overlay *ovl = ovid->overlays[j]; + + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + ovl->get_overlay_info(ovl, &info); + info.enabled = 1; + info.paddr = addr; + if (ovl->set_overlay_info(ovl, &info)) { + ret = -EINVAL; + goto streamon_err1; + } + } + } + + /* First save the configuration in ovelray structure */ + ret = omapvid_init(vout, addr); + if (ret) + v4l2_err(&vout->vid_dev->v4l2_dev, + "failed to set overlay info\n"); + /* Enable the pipeline and set the Go bit */ + ret = omapvid_apply_changes(vout); + if (ret) + v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n"); + + ret = 0; + +streamon_err1: + if (ret) + ret = videobuf_streamoff(q); +streamon_err: + mutex_unlock(&vout->lock); + return ret; +} + +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + u32 mask = 0; + int ret = 0, j; + struct omap_vout_device *vout = fh; + struct omapvideo_info *ovid = &vout->vid_info; + + if (!vout->streaming) + return -EINVAL; + + vout->streaming = 0; + mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD; + + omap_dispc_unregister_isr(omap_vout_isr, vout, mask); + + for (j = 0; j < ovid->num_overlays; j++) { + struct omap_overlay *ovl = ovid->overlays[j]; + + if (ovl->manager && ovl->manager->device) { + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + info.enabled = 0; + ret = ovl->set_overlay_info(ovl, &info); + if (ret) + v4l2_err(&vout->vid_dev->v4l2_dev, + "failed to update overlay info in streamoff\n"); + } + } + + /* Turn of the pipeline */ + ret = omapvid_apply_changes(vout); + if (ret) + v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode in" + " streamoff\n"); + + INIT_LIST_HEAD(&vout->dma_queue); + ret = videobuf_streamoff(&vout->vbq); + + return ret; +} + +static int vidioc_s_fbuf(struct file *file, void *fh, + struct v4l2_framebuffer *a) +{ + int enable = 0; + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = fh; + struct omap_overlay_manager_info info; + enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + /* OMAP DSS doesn't support Source and Destination color + key together */ + if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) && + (a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) + return -EINVAL; + /* OMAP DSS Doesn't support the Destination color key + and alpha blending together */ + if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY) && + (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA)) + return -EINVAL; + + if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY; + key_type = OMAP_DSS_COLOR_KEY_VID_SRC; + } else + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY; + + if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY; + key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + } else + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY; + + if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY | + V4L2_FBUF_FLAG_SRC_CHROMAKEY)) + enable = 1; + else + enable = 0; + if (ovl->manager && ovl->manager->get_manager_info && + ovl->manager->set_manager_info) { + + ovl->manager->get_manager_info(ovl->manager, &info); + info.trans_enabled = enable; + info.trans_key_type = key_type; + info.trans_key = vout->win.chromakey; + + if (ovl->manager->set_manager_info(ovl->manager, &info)) + return -EINVAL; + } + if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) { + vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + enable = 1; + } else { + vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA; + enable = 0; + } + if (ovl->manager && ovl->manager->get_manager_info && + ovl->manager->set_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + info.alpha_enabled = enable; + if (ovl->manager->set_manager_info(ovl->manager, &info)) + return -EINVAL; + } + + return 0; +} + +static int vidioc_g_fbuf(struct file *file, void *fh, + struct v4l2_framebuffer *a) +{ + struct omap_overlay *ovl; + struct omapvideo_info *ovid; + struct omap_vout_device *vout = fh; + struct omap_overlay_manager_info info; + + ovid = &vout->vid_info; + ovl = ovid->overlays[0]; + + a->flags = 0x0; + a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY + | V4L2_FBUF_CAP_SRC_CHROMAKEY; + + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC) + a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY; + if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST) + a->flags |= V4L2_FBUF_FLAG_CHROMAKEY; + } + if (ovl->manager && ovl->manager->get_manager_info) { + ovl->manager->get_manager_info(ovl->manager, &info); + if (info.alpha_enabled) + a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + } + + return 0; +} + +static const struct v4l2_ioctl_ops vout_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + +static const struct v4l2_file_operations omap_vout_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .mmap = omap_vout_mmap, + .open = omap_vout_open, + .release = omap_vout_release, +}; + +/* Init functions used during driver initialization */ +/* Initial setup of video_data */ +static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) +{ + struct video_device *vfd; + struct v4l2_pix_format *pix; + struct v4l2_control *control; + struct omap_dss_device *display = + vout->vid_info.overlays[0]->manager->device; + + /* set the default pix */ + pix = &vout->pix; + + /* Set the default picture of QVGA */ + pix->width = QQVGA_WIDTH; + pix->height = QQVGA_HEIGHT; + + /* Default pixel format is RGB 5-6-5 */ + pix->pixelformat = V4L2_PIX_FMT_RGB565; + pix->field = V4L2_FIELD_ANY; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + pix->colorspace = V4L2_COLORSPACE_JPEG; + + vout->bpp = RGB565_BPP; + vout->fbuf.fmt.width = display->panel.timings.x_res; + vout->fbuf.fmt.height = display->panel.timings.y_res; + + /* Set the data structures for the overlay parameters*/ + vout->win.global_alpha = 255; + vout->fbuf.flags = 0; + vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA | + V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY; + vout->win.chromakey = 0; + + omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win); + + /*Initialize the control variables for + rotation, flipping and background color. */ + control = vout->control; + control[0].id = V4L2_CID_ROTATE; + control[0].value = 0; + vout->rotation = 0; + vout->mirror = 0; + vout->control[2].id = V4L2_CID_HFLIP; + vout->control[2].value = 0; + vout->vrfb_bpp = 2; + + control[1].id = V4L2_CID_BG_COLOR; + control[1].value = 0; + + /* initialize the video_device struct */ + vfd = vout->vfd = video_device_alloc(); + + if (!vfd) { + printk(KERN_ERR VOUT_NAME ": could not allocate" + " video device struct\n"); + return -ENOMEM; + } + vfd->release = video_device_release; + vfd->ioctl_ops = &vout_ioctl_ops; + + strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name)); + + /* need to register for a VID_HARDWARE_* ID in videodev.h */ + vfd->fops = &omap_vout_fops; + vfd->v4l2_dev = &vout->vid_dev->v4l2_dev; + mutex_init(&vout->lock); + + vfd->minor = -1; + return 0; + +} + +/* Setup video buffers */ +static int __init omap_vout_setup_video_bufs(struct platform_device *pdev, + int vid_num) +{ + u32 numbuffers; + int ret = 0, i, j; + int image_width, image_height; + struct video_device *vfd; + struct omap_vout_device *vout; + int static_vrfb_allocation = 0, vrfb_num_bufs = VRFB_NUM_BUFS; + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct omap2video_device *vid_dev = + container_of(v4l2_dev, struct omap2video_device, v4l2_dev); + + vout = vid_dev->vouts[vid_num]; + vfd = vout->vfd; + + numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers; + vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize; + dev_info(&pdev->dev, "Buffer Size = %d\n", vout->buffer_size); + + for (i = 0; i < numbuffers; i++) { + vout->buf_virt_addr[i] = + omap_vout_alloc_buffer(vout->buffer_size, + (u32 *) &vout->buf_phy_addr[i]); + if (!vout->buf_virt_addr[i]) { + numbuffers = i; + ret = -ENOMEM; + goto free_buffers; + } + } + + for (i = 0; i < VRFB_NUM_BUFS; i++) { + if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) { + dev_info(&pdev->dev, ": VRFB allocation failed\n"); + for (j = 0; j < i; j++) + omap_vrfb_release_ctx(&vout->vrfb_context[j]); + ret = -ENOMEM; + goto free_buffers; + } + } + vout->cropped_offset = 0; + + /* Calculate VRFB memory size */ + /* allocate for worst case size */ + image_width = VID_MAX_WIDTH / TILE_SIZE; + if (VID_MAX_WIDTH % TILE_SIZE) + image_width++; + + image_width = image_width * TILE_SIZE; + image_height = VID_MAX_HEIGHT / TILE_SIZE; + + if (VID_MAX_HEIGHT % TILE_SIZE) + image_height++; + + image_height = image_height * TILE_SIZE; + vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2); + + /* + * Request and Initialize DMA, for DMA based VRFB transfer + */ + vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE; + vout->vrfb_dma_tx.dma_ch = -1; + vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED; + ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX", + omap_vout_vrfb_dma_tx_callback, + (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch); + if (ret < 0) { + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + dev_info(&pdev->dev, ": failed to allocate DMA Channel for" + " video%d\n", vfd->minor); + } + init_waitqueue_head(&vout->vrfb_dma_tx.wait); + + /* Allocate VRFB buffers if selected through bootargs */ + static_vrfb_allocation = (vid_num == 0) ? + vid1_static_vrfb_alloc : vid2_static_vrfb_alloc; + + /* statically allocated the VRFB buffer is done through + commands line aruments */ + if (static_vrfb_allocation) { + if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) { + ret = -ENOMEM; + goto release_vrfb_ctx; + } + vout->vrfb_static_allocation = 1; + } + return 0; + +release_vrfb_ctx: + for (j = 0; j < VRFB_NUM_BUFS; j++) + omap_vrfb_release_ctx(&vout->vrfb_context[j]); + +free_buffers: + for (i = 0; i < numbuffers; i++) { + omap_vout_free_buffer(vout->buf_virt_addr[i], + vout->buffer_size); + vout->buf_virt_addr[i] = 0; + vout->buf_phy_addr[i] = 0; + } + return ret; + +} + +/* Create video out devices */ +static int __init omap_vout_create_video_devices(struct platform_device *pdev) +{ + int ret = 0, k; + struct omap_vout_device *vout; + struct video_device *vfd = NULL; + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct omap2video_device *vid_dev = container_of(v4l2_dev, + struct omap2video_device, v4l2_dev); + + for (k = 0; k < pdev->num_resources; k++) { + + vout = kmalloc(sizeof(struct omap_vout_device), GFP_KERNEL); + if (!vout) { + dev_err(&pdev->dev, ": could not allocate memory\n"); + return -ENOMEM; + } + memset(vout, 0, sizeof(struct omap_vout_device)); + + vout->vid = k; + vid_dev->vouts[k] = vout; + vout->vid_dev = vid_dev; + /* Select video2 if only 1 overlay is controlled by V4L2 */ + if (pdev->num_resources == 1) + vout->vid_info.overlays[0] = vid_dev->overlays[k + 2]; + else + /* Else select video1 and video2 one by one. */ + vout->vid_info.overlays[0] = vid_dev->overlays[k + 1]; + vout->vid_info.num_overlays = 1; + vout->vid_info.id = k + 1; + + /* Setup the default configuration for the video devices + */ + if (omap_vout_setup_video_data(vout) != 0) { + ret = -ENOMEM; + goto error; + } + + /* Allocate default number of buffers for the video streaming + * and reserve the VRFB space for rotation + */ + if (omap_vout_setup_video_bufs(pdev, k) != 0) { + ret = -ENOMEM; + goto error1; + } + + /* Register the Video device with V4L2 + */ + vfd = vout->vfd; + if (video_register_device(vfd, VFL_TYPE_GRABBER, k + 1) < 0) { + dev_err(&pdev->dev, ": Could not register " + "Video for Linux device\n"); + vfd->minor = -1; + ret = -ENODEV; + goto error2; + } + video_set_drvdata(vfd, vout); + + /* Configure the overlay structure */ + ret = omapvid_init(vid_dev->vouts[k], 0); + if (!ret) + goto success; + +error2: + omap_vout_release_vrfb(vout); + omap_vout_free_buffers(vout); +error1: + video_device_release(vfd); +error: + kfree(vout); + return ret; + +success: + dev_info(&pdev->dev, ": registered and initialized" + " video device %d\n", vfd->minor); + if (k == (pdev->num_resources - 1)) + return 0; + } + + return -ENODEV; +} +/* Driver functions */ +static void omap_vout_cleanup_device(struct omap_vout_device *vout) +{ + struct video_device *vfd; + + if (!vout) + return; + + vfd = vout->vfd; + if (vfd) { + if (!video_is_registered(vfd)) { + /* + * The device was never registered, so release the + * video_device struct directly. + */ + video_device_release(vfd); + } else { + /* + * The unregister function will release the video_device + * struct as well as unregistering it. + */ + video_unregister_device(vfd); + } + } + + omap_vout_release_vrfb(vout); + omap_vout_free_buffers(vout); + /* Free the VRFB buffer if allocated + * init time + */ + if (vout->vrfb_static_allocation) + omap_vout_free_vrfb_buffers(vout); + + kfree(vout); +} + +static int omap_vout_remove(struct platform_device *pdev) +{ + int k; + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct omap2video_device *vid_dev = container_of(v4l2_dev, struct + omap2video_device, v4l2_dev); + + v4l2_device_unregister(v4l2_dev); + for (k = 0; k < pdev->num_resources; k++) + omap_vout_cleanup_device(vid_dev->vouts[k]); + + for (k = 0; k < vid_dev->num_displays; k++) { + if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED) + vid_dev->displays[k]->disable(vid_dev->displays[k]); + + omap_dss_put_device(vid_dev->displays[k]); + } + kfree(vid_dev); + return 0; +} + +static int __init omap_vout_probe(struct platform_device *pdev) +{ + int ret = 0, i; + struct omap_overlay *ovl; + struct omap_dss_device *dssdev = NULL; + struct omap_dss_device *def_display; + struct omap2video_device *vid_dev = NULL; + + if (pdev->num_resources == 0) { + dev_err(&pdev->dev, "probed for an unknown device\n"); + return -ENODEV; + } + + vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL); + if (vid_dev == NULL) + return -ENOMEM; + + vid_dev->num_displays = 0; + for_each_dss_dev(dssdev) { + omap_dss_get_device(dssdev); + vid_dev->displays[vid_dev->num_displays++] = dssdev; + } + + if (vid_dev->num_displays == 0) { + dev_err(&pdev->dev, "no displays\n"); + ret = -EINVAL; + goto probe_err0; + } + + vid_dev->num_overlays = omap_dss_get_num_overlays(); + for (i = 0; i < vid_dev->num_overlays; i++) + vid_dev->overlays[i] = omap_dss_get_overlay(i); + + vid_dev->num_managers = omap_dss_get_num_overlay_managers(); + for (i = 0; i < vid_dev->num_managers; i++) + vid_dev->managers[i] = omap_dss_get_overlay_manager(i); + + /* Get the Video1 overlay and video2 overlay. + * Setup the Display attached to that overlays + */ + for (i = 1; i < vid_dev->num_overlays; i++) { + ovl = omap_dss_get_overlay(i); + if (ovl->manager && ovl->manager->device) { + def_display = ovl->manager->device; + } else { + dev_warn(&pdev->dev, "cannot find display\n"); + def_display = NULL; + } + if (def_display) { + ret = def_display->enable(def_display); + if (ret) { + /* Here we are not considering a error + * as display may be enabled by frame + * buffer driver + */ + dev_warn(&pdev->dev, + "'%s' Display already enabled\n", + def_display->name); + } + /* set the update mode */ + if (def_display->caps & + OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { +#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE + if (def_display->enable_te) + def_display->enable_te(def_display, 1); + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); +#else /* MANUAL_UPDATE */ + if (def_display->enable_te) + def_display->enable_te(def_display, 0); + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_MANUAL); +#endif + } else { + if (def_display->set_update_mode) + def_display->set_update_mode(def_display, + OMAP_DSS_UPDATE_AUTO); + } + } + } + + if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) { + dev_err(&pdev->dev, "v4l2_device_register failed\n"); + ret = -ENODEV; + goto probe_err1; + } + + ret = omap_vout_create_video_devices(pdev); + if (ret) + goto probe_err2; + + for (i = 0; i < vid_dev->num_displays; i++) { + struct omap_dss_device *display = vid_dev->displays[i]; + + if (display->update) + display->update(display, 0, 0, + display->panel.timings.x_res, + display->panel.timings.y_res); + } + return 0; + +probe_err2: + v4l2_device_unregister(&vid_dev->v4l2_dev); +probe_err1: + for (i = 1; i < vid_dev->num_overlays; i++) { + def_display = NULL; + ovl = omap_dss_get_overlay(i); + if (ovl->manager && ovl->manager->device) + def_display = ovl->manager->device; + + if (def_display) + def_display->disable(def_display); + } +probe_err0: + kfree(vid_dev); + return ret; +} + +static struct platform_driver omap_vout_driver = { + .driver = { + .name = VOUT_NAME, + }, + .probe = omap_vout_probe, + .remove = omap_vout_remove, +}; + +static int __init omap_vout_init(void) +{ + if (platform_driver_register(&omap_vout_driver) != 0) { + printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n"); + return -EINVAL; + } + return 0; +} + +static void omap_vout_cleanup(void) +{ + platform_driver_unregister(&omap_vout_driver); +} + +late_initcall(omap_vout_init); +module_exit(omap_vout_cleanup); diff --git a/drivers/media/video/omap/omap_voutdef.h b/drivers/media/video/omap/omap_voutdef.h new file mode 100644 index 000000000000..ea3a047f8bca --- /dev/null +++ b/drivers/media/video/omap/omap_voutdef.h @@ -0,0 +1,147 @@ +/* + * omap_voutdef.h + * + * Copyright (C) 2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef OMAP_VOUTDEF_H +#define OMAP_VOUTDEF_H + +#include <plat/display.h> + +#define YUYV_BPP 2 +#define RGB565_BPP 2 +#define RGB24_BPP 3 +#define RGB32_BPP 4 +#define TILE_SIZE 32 +#define YUYV_VRFB_BPP 2 +#define RGB_VRFB_BPP 1 +#define MAX_CID 3 +#define MAC_VRFB_CTXS 4 +#define MAX_VOUT_DEV 2 +#define MAX_OVLS 3 +#define MAX_DISPLAYS 3 +#define MAX_MANAGERS 3 + +/* Enum for Rotation + * DSS understands rotation in 0, 1, 2, 3 context + * while V4L2 driver understands it as 0, 90, 180, 270 + */ +enum dss_rotation { + dss_rotation_0_degree = 0, + dss_rotation_90_degree = 1, + dss_rotation_180_degree = 2, + dss_rotation_270_degree = 3, +}; +/* + * This structure is used to store the DMA transfer parameters + * for VRFB hidden buffer + */ +struct vid_vrfb_dma { + int dev_id; + int dma_ch; + int req_status; + int tx_status; + wait_queue_head_t wait; +}; + +struct omapvideo_info { + int id; + int num_overlays; + struct omap_overlay *overlays[MAX_OVLS]; +}; + +struct omap2video_device { + struct mutex mtx; + + int state; + + struct v4l2_device v4l2_dev; + struct omap_vout_device *vouts[MAX_VOUT_DEV]; + + int num_displays; + struct omap_dss_device *displays[MAX_DISPLAYS]; + int num_overlays; + struct omap_overlay *overlays[MAX_OVLS]; + int num_managers; + struct omap_overlay_manager *managers[MAX_MANAGERS]; +}; + +/* per-device data structure */ +struct omap_vout_device { + + struct omapvideo_info vid_info; + struct video_device *vfd; + struct omap2video_device *vid_dev; + int vid; + int opened; + + /* we don't allow to change image fmt/size once buffer has + * been allocated + */ + int buffer_allocated; + /* allow to reuse previously allocated buffer which is big enough */ + int buffer_size; + /* keep buffer info across opens */ + unsigned long buf_virt_addr[VIDEO_MAX_FRAME]; + unsigned long buf_phy_addr[VIDEO_MAX_FRAME]; + enum omap_color_mode dss_mode; + + /* we don't allow to request new buffer when old buffers are + * still mmaped + */ + int mmap_count; + + spinlock_t vbq_lock; /* spinlock for videobuf queues */ + unsigned long field_count; /* field counter for videobuf_buffer */ + + /* non-NULL means streaming is in progress. */ + bool streaming; + + struct v4l2_pix_format pix; + struct v4l2_rect crop; + struct v4l2_window win; + struct v4l2_framebuffer fbuf; + + /* Lock to protect the shared data structures in ioctl */ + struct mutex lock; + + /* V4L2 control structure for different control id */ + struct v4l2_control control[MAX_CID]; + enum dss_rotation rotation; + bool mirror; + int flicker_filter; + /* V4L2 control structure for different control id */ + + int bpp; /* bytes per pixel */ + int vrfb_bpp; /* bytes per pixel with respect to VRFB */ + + struct vid_vrfb_dma vrfb_dma_tx; + unsigned int smsshado_phy_addr[MAC_VRFB_CTXS]; + unsigned int smsshado_virt_addr[MAC_VRFB_CTXS]; + struct vrfb vrfb_context[MAC_VRFB_CTXS]; + bool vrfb_static_allocation; + unsigned int smsshado_size; + unsigned char pos; + + int ps, vr_ps, line_length, first_int, field_id; + enum v4l2_memory memory; + struct videobuf_buffer *cur_frm, *next_frm; + struct list_head dma_queue; + u8 *queued_buf_addr[VIDEO_MAX_FRAME]; + u32 cropped_offset; + s32 tv_field1_offset; + void *isr_handle; + + /* Buffer queue variables */ + struct omap_vout_device *vout; + enum v4l2_buf_type type; + struct videobuf_queue vbq; + int io_allowed; + +}; +#endif /* ifndef OMAP_VOUTDEF_H */ diff --git a/drivers/media/video/omap/omap_voutlib.c b/drivers/media/video/omap/omap_voutlib.c new file mode 100644 index 000000000000..b941c761eef9 --- /dev/null +++ b/drivers/media/video/omap/omap_voutlib.c @@ -0,0 +1,293 @@ +/* + * omap_voutlib.c + * + * Copyright (C) 2005-2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Based on the OMAP2 camera driver + * Video-for-Linux (Version 2) camera capture driver for + * the OMAP24xx camera controller. + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2010 Texas Instruments. + * + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/videodev2.h> + +#include <plat/cpu.h> + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP Video library"); +MODULE_LICENSE("GPL"); + +/* Return the default overlay cropping rectangle in crop given the image + * size in pix and the video display size in fbuf. The default + * cropping rectangle is the largest rectangle no larger than the capture size + * that will fit on the display. The default cropping rectangle is centered in + * the image. All dimensions and offsets are rounded down to even numbers. + */ +void omap_vout_default_crop(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop) +{ + crop->width = (pix->width < fbuf->fmt.width) ? + pix->width : fbuf->fmt.width; + crop->height = (pix->height < fbuf->fmt.height) ? + pix->height : fbuf->fmt.height; + crop->width &= ~1; + crop->height &= ~1; + crop->left = ((pix->width - crop->width) >> 1) & ~1; + crop->top = ((pix->height - crop->height) >> 1) & ~1; +} +EXPORT_SYMBOL_GPL(omap_vout_default_crop); + +/* Given a new render window in new_win, adjust the window to the + * nearest supported configuration. The adjusted window parameters are + * returned in new_win. + * Returns zero if succesful, or -EINVAL if the requested window is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_try_window(struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win) +{ + struct v4l2_rect try_win; + + /* make a working copy of the new_win rectangle */ + try_win = new_win->w; + + /* adjust the preview window so it fits on the display by clipping any + * offscreen areas + */ + if (try_win.left < 0) { + try_win.width += try_win.left; + try_win.left = 0; + } + if (try_win.top < 0) { + try_win.height += try_win.top; + try_win.top = 0; + } + try_win.width = (try_win.width < fbuf->fmt.width) ? + try_win.width : fbuf->fmt.width; + try_win.height = (try_win.height < fbuf->fmt.height) ? + try_win.height : fbuf->fmt.height; + if (try_win.left + try_win.width > fbuf->fmt.width) + try_win.width = fbuf->fmt.width - try_win.left; + if (try_win.top + try_win.height > fbuf->fmt.height) + try_win.height = fbuf->fmt.height - try_win.top; + try_win.width &= ~1; + try_win.height &= ~1; + + if (try_win.width <= 0 || try_win.height <= 0) + return -EINVAL; + + /* We now have a valid preview window, so go with it */ + new_win->w = try_win; + new_win->field = V4L2_FIELD_ANY; + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_try_window); + +/* Given a new render window in new_win, adjust the window to the + * nearest supported configuration. The image cropping window in crop + * will also be adjusted if necessary. Preference is given to keeping the + * the window as close to the requested configuration as possible. If + * successful, new_win, vout->win, and crop are updated. + * Returns zero if succesful, or -EINVAL if the requested preview window is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_new_window(struct v4l2_rect *crop, + struct v4l2_window *win, struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win) +{ + int err; + + err = omap_vout_try_window(fbuf, new_win); + if (err) + return err; + + /* update our preview window */ + win->w = new_win->w; + win->field = new_win->field; + win->chromakey = new_win->chromakey; + + /* Adjust the cropping window to allow for resizing limitation */ + if (cpu_is_omap24xx()) { + /* For 24xx limit is 8x to 1/2x scaling. */ + if ((crop->height/win->w.height) >= 2) + crop->height = win->w.height * 2; + + if ((crop->width/win->w.width) >= 2) + crop->width = win->w.width * 2; + + if (crop->width > 768) { + /* The OMAP2420 vertical resizing line buffer is 768 + * pixels wide. If the cropped image is wider than + * 768 pixels then it cannot be vertically resized. + */ + if (crop->height != win->w.height) + crop->width = 768; + } + } else if (cpu_is_omap34xx()) { + /* For 34xx limit is 8x to 1/4x scaling. */ + if ((crop->height/win->w.height) >= 4) + crop->height = win->w.height * 4; + + if ((crop->width/win->w.width) >= 4) + crop->width = win->w.width * 4; + } + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_new_window); + +/* Given a new cropping rectangle in new_crop, adjust the cropping rectangle to + * the nearest supported configuration. The image render window in win will + * also be adjusted if necessary. The preview window is adjusted such that the + * horizontal and vertical rescaling ratios stay constant. If the render + * window would fall outside the display boundaries, the cropping rectangle + * will also be adjusted to maintain the rescaling ratios. If successful, crop + * and win are updated. + * Returns zero if succesful, or -EINVAL if the requested cropping rectangle is + * impossible and cannot reasonably be adjusted. + */ +int omap_vout_new_crop(struct v4l2_pix_format *pix, + struct v4l2_rect *crop, struct v4l2_window *win, + struct v4l2_framebuffer *fbuf, const struct v4l2_rect *new_crop) +{ + struct v4l2_rect try_crop; + unsigned long vresize, hresize; + + /* make a working copy of the new_crop rectangle */ + try_crop = *new_crop; + + /* adjust the cropping rectangle so it fits in the image */ + if (try_crop.left < 0) { + try_crop.width += try_crop.left; + try_crop.left = 0; + } + if (try_crop.top < 0) { + try_crop.height += try_crop.top; + try_crop.top = 0; + } + try_crop.width = (try_crop.width < pix->width) ? + try_crop.width : pix->width; + try_crop.height = (try_crop.height < pix->height) ? + try_crop.height : pix->height; + if (try_crop.left + try_crop.width > pix->width) + try_crop.width = pix->width - try_crop.left; + if (try_crop.top + try_crop.height > pix->height) + try_crop.height = pix->height - try_crop.top; + + try_crop.width &= ~1; + try_crop.height &= ~1; + + if (try_crop.width <= 0 || try_crop.height <= 0) + return -EINVAL; + + if (cpu_is_omap24xx()) { + if (crop->height != win->w.height) { + /* If we're resizing vertically, we can't support a + * crop width wider than 768 pixels. + */ + if (try_crop.width > 768) + try_crop.width = 768; + } + } + /* vertical resizing */ + vresize = (1024 * crop->height) / win->w.height; + if (cpu_is_omap24xx() && (vresize > 2048)) + vresize = 2048; + else if (cpu_is_omap34xx() && (vresize > 4096)) + vresize = 4096; + + win->w.height = ((1024 * try_crop.height) / vresize) & ~1; + if (win->w.height == 0) + win->w.height = 2; + if (win->w.height + win->w.top > fbuf->fmt.height) { + /* We made the preview window extend below the bottom of the + * display, so clip it to the display boundary and resize the + * cropping height to maintain the vertical resizing ratio. + */ + win->w.height = (fbuf->fmt.height - win->w.top) & ~1; + if (try_crop.height == 0) + try_crop.height = 2; + } + /* horizontal resizing */ + hresize = (1024 * crop->width) / win->w.width; + if (cpu_is_omap24xx() && (hresize > 2048)) + hresize = 2048; + else if (cpu_is_omap34xx() && (hresize > 4096)) + hresize = 4096; + + win->w.width = ((1024 * try_crop.width) / hresize) & ~1; + if (win->w.width == 0) + win->w.width = 2; + if (win->w.width + win->w.left > fbuf->fmt.width) { + /* We made the preview window extend past the right side of the + * display, so clip it to the display boundary and resize the + * cropping width to maintain the horizontal resizing ratio. + */ + win->w.width = (fbuf->fmt.width - win->w.left) & ~1; + if (try_crop.width == 0) + try_crop.width = 2; + } + if (cpu_is_omap24xx()) { + if ((try_crop.height/win->w.height) >= 2) + try_crop.height = win->w.height * 2; + + if ((try_crop.width/win->w.width) >= 2) + try_crop.width = win->w.width * 2; + + if (try_crop.width > 768) { + /* The OMAP2420 vertical resizing line buffer is + * 768 pixels wide. If the cropped image is wider + * than 768 pixels then it cannot be vertically resized. + */ + if (try_crop.height != win->w.height) + try_crop.width = 768; + } + } else if (cpu_is_omap34xx()) { + if ((try_crop.height/win->w.height) >= 4) + try_crop.height = win->w.height * 4; + + if ((try_crop.width/win->w.width) >= 4) + try_crop.width = win->w.width * 4; + } + /* update our cropping rectangle and we're done */ + *crop = try_crop; + return 0; +} +EXPORT_SYMBOL_GPL(omap_vout_new_crop); + +/* Given a new format in pix and fbuf, crop and win + * structures are initialized to default values. crop + * is initialized to the largest window size that will fit on the display. The + * crop window is centered in the image. win is initialized to + * the same size as crop and is centered on the display. + * All sizes and offsets are constrained to be even numbers. + */ +void omap_vout_new_format(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop, + struct v4l2_window *win) +{ + /* crop defines the preview source window in the image capture + * buffer + */ + omap_vout_default_crop(pix, fbuf, crop); + + /* win defines the preview target window on the display */ + win->w.width = crop->width; + win->w.height = crop->height; + win->w.left = ((fbuf->fmt.width - win->w.width) >> 1) & ~1; + win->w.top = ((fbuf->fmt.height - win->w.height) >> 1) & ~1; +} +EXPORT_SYMBOL_GPL(omap_vout_new_format); + diff --git a/drivers/media/video/omap/omap_voutlib.h b/drivers/media/video/omap/omap_voutlib.h new file mode 100644 index 000000000000..a60b16e8bfc3 --- /dev/null +++ b/drivers/media/video/omap/omap_voutlib.h @@ -0,0 +1,34 @@ +/* + * omap_voutlib.h + * + * Copyright (C) 2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef OMAP_VOUTLIB_H +#define OMAP_VOUTLIB_H + +extern void omap_vout_default_crop(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop); + +extern int omap_vout_new_crop(struct v4l2_pix_format *pix, + struct v4l2_rect *crop, struct v4l2_window *win, + struct v4l2_framebuffer *fbuf, + const struct v4l2_rect *new_crop); + +extern int omap_vout_try_window(struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win); + +extern int omap_vout_new_window(struct v4l2_rect *crop, + struct v4l2_window *win, struct v4l2_framebuffer *fbuf, + struct v4l2_window *new_win); + +extern void omap_vout_new_format(struct v4l2_pix_format *pix, + struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop, + struct v4l2_window *win); +#endif /* #ifndef OMAP_VOUTLIB_H */ + diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index ce76d952e161..f85b2ed8a2d8 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -452,8 +452,8 @@ static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, *size = fh->pix.sizeimage; /* accessing fh->cam->capture_mem is ok, it's constant */ - while (*size * *cnt > fh->cam->capture_mem) - (*cnt)--; + if (*size * *cnt > fh->cam->capture_mem) + *cnt = fh->cam->capture_mem / *size; return 0; } diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index e0bce8dc74bf..a10912097b7a 100644 --- a/drivers/media/video/ov511.c +++ b/drivers/media/video/ov511.c @@ -57,8 +57,8 @@ #define DRIVER_VERSION "v1.64 for Linux 2.5" #define EMAIL "mark@alpha.dyndns.org" #define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org> & Bret Wallach \ - & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \ - <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>" +& Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \ +<cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>" #define DRIVER_DESC "ov511 USB Camera Driver" #define OV511_I2C_RETRIES 3 @@ -5916,11 +5916,6 @@ ov51x_disconnect(struct usb_interface *intf) mutex_lock(&ov->lock); usb_set_intfdata (intf, NULL); - if (!ov) { - mutex_unlock(&ov->lock); - return; - } - /* Free device number */ ov511_devused &= ~(1 << ov->nr); @@ -5945,7 +5940,7 @@ ov51x_disconnect(struct usb_interface *intf) ov->dev = NULL; /* Free the memory */ - if (ov && !ov->user) { + if (!ov->user) { mutex_lock(&ov->cbuf_lock); kfree(ov->cbuf); ov->cbuf = NULL; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index aaa50f9b8e78..91c886ab15c6 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -198,6 +198,7 @@ struct ov7670_info { struct ov7670_format_struct *fmt; /* Current format */ unsigned char sat; /* Saturation value */ int hue; /* Hue value */ + u8 clkrc; /* Clock divider value */ }; static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) @@ -351,7 +352,7 @@ static struct regval_list ov7670_default_regs[] = { static struct regval_list ov7670_fmt_yuv422[] = { { REG_COM7, 0x0 }, /* Selects YUV mode */ { REG_RGB444, 0 }, /* No RGB444 please */ - { REG_COM1, 0 }, + { REG_COM1, 0 }, /* CCIR601 */ { REG_COM15, COM15_R00FF }, { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0x80 }, /* "matrix coefficient 1" */ @@ -367,7 +368,7 @@ static struct regval_list ov7670_fmt_yuv422[] = { static struct regval_list ov7670_fmt_rgb565[] = { { REG_COM7, COM7_RGB }, /* Selects RGB mode */ { REG_RGB444, 0 }, /* No RGB444 please */ - { REG_COM1, 0x0 }, + { REG_COM1, 0x0 }, /* CCIR601 */ { REG_COM15, COM15_RGB565 }, { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ @@ -383,7 +384,7 @@ static struct regval_list ov7670_fmt_rgb565[] = { static struct regval_list ov7670_fmt_rgb444[] = { { REG_COM7, COM7_RGB }, /* Selects RGB mode */ { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ - { REG_COM1, 0x40 }, /* Magic reserved bit */ + { REG_COM1, 0x0 }, /* CCIR601 */ { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ @@ -408,8 +409,13 @@ static struct regval_list ov7670_fmt_raw[] = { /* * Low-level register I/O. + * + * Note that there are two versions of these. On the XO 1, the + * i2c controller only does SMBUS, so that's what we use. The + * ov7670 is not really an SMBUS device, though, so the communication + * is not always entirely reliable. */ - +#ifdef CONFIG_OLPC_XO_1 static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, unsigned char *value) { @@ -432,9 +438,67 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, int ret = i2c_smbus_write_byte_data(client, reg, value); if (reg == REG_COM7 && (value & COM7_RESET)) - msleep(2); /* Wait for reset to run */ + msleep(5); /* Wait for reset to run */ + return ret; +} + +#else /* ! CONFIG_OLPC_XO_1 */ +/* + * On most platforms, we'd rather do straight i2c I/O. + */ +static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 data = reg; + struct i2c_msg msg; + int ret; + + /* + * Send out the register address... + */ + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + msg.buf = &data; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + printk(KERN_ERR "Error %d on register write\n", ret); + return ret; + } + /* + * ...then read back the result. + */ + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) { + *value = data; + ret = 0; + } + return ret; +} + + +static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_msg msg; + unsigned char data[2] = { reg, value }; + int ret; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret > 0) + ret = 0; + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(5); /* Wait for reset to run */ return ret; } +#endif /* CONFIG_OLPC_XO_1 */ /* @@ -744,22 +808,12 @@ static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) struct ov7670_format_struct *ovfmt; struct ov7670_win_size *wsize; struct ov7670_info *info = to_state(sd); - unsigned char com7, clkrc = 0; + unsigned char com7; ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); if (ret) return ret; /* - * HACK: if we're running rgb565 we need to grab then rewrite - * CLKRC. If we're *not*, however, then rewriting clkrc hoses - * the colors. - */ - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { - ret = ov7670_read(sd, REG_CLKRC, &clkrc); - if (ret) - return ret; - } - /* * COM7 is a pain in the ass, it doesn't like to be read then * quickly written afterward. But we have everything we need * to set it absolutely here, as long as the format-specific @@ -779,8 +833,18 @@ static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) ret = ov7670_write_array(sd, wsize->regs); info->fmt = ovfmt; - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 && ret == 0) - ret = ov7670_write(sd, REG_CLKRC, clkrc); + /* + * If we're running RGB565, we must rewrite clkrc after setting + * the other parameters or the image looks poor. If we're *not* + * doing RGB565, we must not rewrite clkrc or the image looks + * *really* poor. + * + * (Update) Now that we retain clkrc state, we should be able + * to write it unconditionally, and that will make the frame + * rate persistent too. + */ + if (ret == 0) + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); return ret; } @@ -791,20 +855,17 @@ static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct v4l2_captureparm *cp = &parms->parm.capture; - unsigned char clkrc; - int ret; + struct ov7670_info *info = to_state(sd); if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ov7670_read(sd, REG_CLKRC, &clkrc); - if (ret < 0) - return ret; + memset(cp, 0, sizeof(struct v4l2_captureparm)); cp->capability = V4L2_CAP_TIMEPERFRAME; cp->timeperframe.numerator = 1; cp->timeperframe.denominator = OV7670_FRAME_RATE; - if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1) - cp->timeperframe.denominator /= (clkrc & CLK_SCALE); + if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); return 0; } @@ -812,19 +873,14 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct v4l2_captureparm *cp = &parms->parm.capture; struct v4l2_fract *tpf = &cp->timeperframe; - unsigned char clkrc; - int ret, div; + struct ov7670_info *info = to_state(sd); + int div; if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (cp->extendedmode != 0) return -EINVAL; - /* - * CLKRC has a reserved bit, so let's preserve it. - */ - ret = ov7670_read(sd, REG_CLKRC, &clkrc); - if (ret < 0) - return ret; + if (tpf->numerator == 0 || tpf->denominator == 0) div = 1; /* Reset to full rate */ else @@ -833,10 +889,10 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) div = 1; else if (div > CLK_SCALE) div = CLK_SCALE; - clkrc = (clkrc & 0x80) | div; + info->clkrc = (info->clkrc & 0x80) | div; tpf->numerator = 1; tpf->denominator = OV7670_FRAME_RATE/div; - return ov7670_write(sd, REG_CLKRC, clkrc); + return ov7670_write(sd, REG_CLKRC, info->clkrc); } @@ -1115,6 +1171,140 @@ static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) return ret; } +/* + * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes + * the data sheet, the VREF parts should be the most significant, but + * experience shows otherwise. There seems to be little value in + * messing with the VREF bits, so we leave them alone. + */ +static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char gain; + + ret = ov7670_read(sd, REG_GAIN, &gain); + *value = gain; + return ret; +} + +static int ov7670_s_gain(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com8; + + ret = ov7670_write(sd, REG_GAIN, value & 0xff); + /* Have to turn off AGC as well */ + if (ret == 0) { + ret = ov7670_read(sd, REG_COM8, &com8); + ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); + } + return ret; +} + +/* + * Tweak autogain. + */ +static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + *value = (com8 & COM8_AGC) != 0; + return ret; +} + +static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret == 0) { + if (value) + com8 |= COM8_AGC; + else + com8 &= ~COM8_AGC; + ret = ov7670_write(sd, REG_COM8, com8); + } + return ret; +} + +/* + * Exposure is spread all over the place: top 6 bits in AECHH, middle + * 8 in AECH, and two stashed in COM1 just for the hell of it. + */ +static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com1, aech, aechh; + + ret = ov7670_read(sd, REG_COM1, &com1) + + ov7670_read(sd, REG_AECH, &aech) + + ov7670_read(sd, REG_AECHH, &aechh); + *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); + return ret; +} + +static int ov7670_s_exp(struct v4l2_subdev *sd, int value) +{ + int ret; + unsigned char com1, com8, aech, aechh; + + ret = ov7670_read(sd, REG_COM1, &com1) + + ov7670_read(sd, REG_COM8, &com8); + ov7670_read(sd, REG_AECHH, &aechh); + if (ret) + return ret; + + com1 = (com1 & 0xfc) | (value & 0x03); + aech = (value >> 2) & 0xff; + aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); + ret = ov7670_write(sd, REG_COM1, com1) + + ov7670_write(sd, REG_AECH, aech) + + ov7670_write(sd, REG_AECHH, aechh); + /* Have to turn off AEC as well */ + if (ret == 0) + ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); + return ret; +} + +/* + * Tweak autoexposure. + */ +static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) +{ + int ret; + unsigned char com8; + enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (com8 & COM8_AEC) + *atype = V4L2_EXPOSURE_AUTO; + else + *atype = V4L2_EXPOSURE_MANUAL; + return ret; +} + +static int ov7670_s_autoexp(struct v4l2_subdev *sd, + enum v4l2_exposure_auto_type value) +{ + int ret; + unsigned char com8; + + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret == 0) { + if (value == V4L2_EXPOSURE_AUTO) + com8 |= COM8_AEC; + else + com8 &= ~COM8_AEC; + ret = ov7670_write(sd, REG_COM8, com8); + } + return ret; +} + + + static int ov7670_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { @@ -1131,6 +1321,14 @@ static int ov7670_queryctrl(struct v4l2_subdev *sd, return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); case V4L2_CID_HUE: return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); + case V4L2_CID_GAIN: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_AUTOGAIN: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + case V4L2_CID_EXPOSURE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); + case V4L2_CID_EXPOSURE_AUTO: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); } return -EINVAL; } @@ -1150,6 +1348,14 @@ static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return ov7670_g_vflip(sd, &ctrl->value); case V4L2_CID_HFLIP: return ov7670_g_hflip(sd, &ctrl->value); + case V4L2_CID_GAIN: + return ov7670_g_gain(sd, &ctrl->value); + case V4L2_CID_AUTOGAIN: + return ov7670_g_autogain(sd, &ctrl->value); + case V4L2_CID_EXPOSURE: + return ov7670_g_exp(sd, &ctrl->value); + case V4L2_CID_EXPOSURE_AUTO: + return ov7670_g_autoexp(sd, &ctrl->value); } return -EINVAL; } @@ -1169,6 +1375,15 @@ static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return ov7670_s_vflip(sd, ctrl->value); case V4L2_CID_HFLIP: return ov7670_s_hflip(sd, ctrl->value); + case V4L2_CID_GAIN: + return ov7670_s_gain(sd, ctrl->value); + case V4L2_CID_AUTOGAIN: + return ov7670_s_autogain(sd, ctrl->value); + case V4L2_CID_EXPOSURE: + return ov7670_s_exp(sd, ctrl->value); + case V4L2_CID_EXPOSURE_AUTO: + return ov7670_s_autoexp(sd, + (enum v4l2_exposure_auto_type) ctrl->value); } return -EINVAL; } @@ -1268,6 +1483,7 @@ static int ov7670_probe(struct i2c_client *client, info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ + info->clkrc = 1; /* 30fps */ return 0; } diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index 47bf60ceb7a2..36599a65f548 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -59,9 +59,9 @@ static const struct ov9640_reg ov9640_regs_dflt[] = { * COM12 |= OV9640_COM12_YUV_AVG * * for RGB, alter the following registers: - * COM7 |= OV9640_COM7_RGB - * COM13 |= OV9640_COM13_RGB_AVG - * COM15 |= proper RGB color encoding mode + * COM7 |= OV9640_COM7_RGB + * COM13 |= OV9640_COM13_RGB_AVG + * COM15 |= proper RGB color encoding mode */ static const struct ov9640_reg ov9640_regs_qqcif[] = { { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 0598bbd3f368..7129b50757db 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -61,7 +61,6 @@ struct pms { int depth; int input; s32 brightness, saturation, hue, contrast; - unsigned long in_use; struct mutex lock; int i2c_count; struct i2c_info i2cinfo[64]; @@ -931,25 +930,8 @@ static ssize_t pms_read(struct file *file, char __user *buf, return len; } -static int pms_exclusive_open(struct file *file) -{ - struct pms *dev = video_drvdata(file); - - return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0; -} - -static int pms_exclusive_release(struct file *file) -{ - struct pms *dev = video_drvdata(file); - - clear_bit(0, &dev->in_use); - return 0; -} - static const struct v4l2_file_operations pms_fops = { .owner = THIS_MODULE, - .open = pms_exclusive_open, - .release = pms_exclusive_release, .ioctl = video_ioctl2, .read = pms_read, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 712b300f723f..301ef197d038 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2028,7 +2028,7 @@ static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw) memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, - video, s_fmt, &fmt); + vbi, s_sliced_fmt, &fmt.fmt.sliced); } diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index bf1e0fe9f4d2..5ffa0d2b0b0d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -49,7 +49,7 @@ struct pvr2_v4l2_dev { struct pvr2_v4l2_fh { struct pvr2_channel channel; - struct pvr2_v4l2_dev *dev_info; + struct pvr2_v4l2_dev *pdi; enum v4l2_priority prio; struct pvr2_ioread *rhp; struct file *file; @@ -162,7 +162,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_v4l2 *vp = fh->vhead; - struct pvr2_v4l2_dev *dev_info = fh->dev_info; + struct pvr2_v4l2_dev *pdi = fh->pdi; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; long ret = -EINVAL; @@ -183,7 +183,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_INPUT: case VIDIOC_S_TUNER: case VIDIOC_S_FREQUENCY: - ret = v4l2_prio_check(&vp->prio, &fh->prio); + ret = v4l2_prio_check(&vp->prio, fh->prio); if (ret) return ret; } @@ -564,14 +564,14 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_STREAMON: { - if (!fh->dev_info->stream) { + if (!fh->pdi->stream) { /* No stream defined for this node. This means that we're not currently allowed to stream from this node. */ ret = -EPERM; break; } - ret = pvr2_hdw_set_stream_type(hdw,dev_info->config); + ret = pvr2_hdw_set_stream_type(hdw,pdi->config); if (ret < 0) return ret; ret = pvr2_hdw_set_streaming(hdw,!0); break; @@ -579,7 +579,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_STREAMOFF: { - if (!fh->dev_info->stream) { + if (!fh->pdi->stream) { /* No stream defined for this node. This means that we're not currently allowed to stream from this node. */ @@ -972,7 +972,7 @@ static int pvr2_v4l2_release(struct file *file) fhp->rhp = NULL; } - v4l2_prio_close(&vp->prio, &fhp->prio); + v4l2_prio_close(&vp->prio, fhp->prio); file->private_data = NULL; if (fhp->vnext) { @@ -1032,7 +1032,7 @@ static int pvr2_v4l2_open(struct file *file) } init_waitqueue_head(&fhp->wait_data); - fhp->dev_info = dip; + fhp->pdi = dip; pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); pvr2_channel_init(&fhp->channel,vp->channel.mc_head); @@ -1093,7 +1093,7 @@ static int pvr2_v4l2_open(struct file *file) fhp->file = file; file->private_data = fhp; - v4l2_prio_open(&vp->prio,&fhp->prio); + v4l2_prio_open(&vp->prio, &fhp->prio); fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw); @@ -1113,7 +1113,7 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) struct pvr2_hdw *hdw; if (fh->rhp) return 0; - if (!fh->dev_info->stream) { + if (!fh->pdi->stream) { /* No stream defined for this node. This means that we're not currently allowed to stream from this node. */ return -EPERM; @@ -1122,21 +1122,21 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) /* First read() attempt. Try to claim the stream and start it... */ if ((ret = pvr2_channel_claim_stream(&fh->channel, - fh->dev_info->stream)) != 0) { + fh->pdi->stream)) != 0) { /* Someone else must already have it */ return ret; } - fh->rhp = pvr2_channel_create_mpeg_stream(fh->dev_info->stream); + fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream); if (!fh->rhp) { pvr2_channel_claim_stream(&fh->channel,NULL); return -ENOMEM; } hdw = fh->channel.mc_head->hdw; - sp = fh->dev_info->stream->stream; + sp = fh->pdi->stream->stream; pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh); - pvr2_hdw_set_stream_type(hdw,fh->dev_info->config); + pvr2_hdw_set_stream_type(hdw,fh->pdi->config); if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret; return pvr2_ioread_set_enabled(fh->rhp,!0); } diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig index 340f954aba34..11980db22d31 100644 --- a/drivers/media/video/pwc/Kconfig +++ b/drivers/media/video/pwc/Kconfig @@ -39,7 +39,7 @@ config USB_PWC_DEBUG config USB_PWC_INPUT_EVDEV bool "USB Philips Cameras input events device support" default y - depends on USB_PWC=INPUT || INPUT=y + depends on USB_PWC && (USB_PWC=INPUT || INPUT=y) ---help--- This option makes USB Philips cameras register the snapshot button as an input device to report button events. diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 04bf5c11308d..7fe70e718656 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -253,8 +253,8 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (0 == *count) *count = 32; - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 9277194cd821..bbd9c11e2c5a 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -555,15 +555,15 @@ static int rj54n1_commit(struct i2c_client *client) return ret; } -static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, - u32 *out_w, u32 *out_h); +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, + s32 *out_w, s32 *out_h); static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { struct i2c_client *client = sd->priv; struct rj54n1 *rj54n1 = to_rj54n1(client); struct v4l2_rect *rect = &a->c; - unsigned int dummy = 0, output_w, output_h, + int dummy = 0, output_w, output_h, input_w = rect->width, input_h = rect->height; int ret; @@ -577,7 +577,7 @@ static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; - dev_dbg(&client->dev, "Scaling for %ux%u : %u = %ux%u\n", + dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n", input_w, input_h, rj54n1->resize, output_w, output_h); ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); @@ -638,8 +638,8 @@ static int rj54n1_g_fmt(struct v4l2_subdev *sd, * the output one, updates the window sizes and returns an error or the resize * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. */ -static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, - u32 *out_w, u32 *out_h) +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, + s32 *out_w, s32 *out_h) { struct i2c_client *client = sd->priv; struct rj54n1 *rj54n1 = to_rj54n1(client); @@ -749,7 +749,7 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, * improve the image quality or stability for larger frames (see comment * above), but I didn't check the framerate. */ - skip = min(resize / 1024, (unsigned)15); + skip = min(resize / 1024, 15U); inc_sel = 1 << skip; @@ -819,7 +819,7 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, *out_w = output_w; *out_h = output_h; - dev_dbg(&client->dev, "Scaled for %ux%u : %u = %ux%u, skip %u\n", + dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n", *in_w, *in_h, resize, output_w, output_h, skip); return resize; @@ -1017,7 +1017,7 @@ static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct i2c_client *client = sd->priv; struct rj54n1 *rj54n1 = to_rj54n1(client); const struct rj54n1_datafmt *fmt; - unsigned int output_w, output_h, max_w, max_h, + int output_w, output_h, max_w, max_h, input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; int ret; diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 3de914deb8ee..3c7a79f3812a 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -1,7 +1,7 @@ /* * s2255drv.c - a driver for the Sensoray 2255 USB video capture device * - * Copyright (C) 2007-2008 by Sensoray Company Inc. + * Copyright (C) 2007-2010 by Sensoray Company Inc. * Dean Anderson * * Some video buffer code based on vivi driver: @@ -52,14 +52,19 @@ #include <linux/smp_lock.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <linux/vmalloc.h> #include <linux/usb.h> +#define S2255_MAJOR_VERSION 1 +#define S2255_MINOR_VERSION 20 +#define S2255_RELEASE 0 +#define S2255_VERSION KERNEL_VERSION(S2255_MAJOR_VERSION, \ + S2255_MINOR_VERSION, \ + S2255_RELEASE) #define FIRMWARE_FILE_NAME "f2255usb.bin" - - /* default JPEG quality */ #define S2255_DEF_JPEG_QUAL 50 /* vendor request in */ @@ -76,14 +81,14 @@ #define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) #define S2255_DEF_BUFS 16 #define S2255_SETMODE_TIMEOUT 500 -#define MAX_CHANNELS 4 -#define S2255_MARKER_FRAME 0x2255DA4AL -#define S2255_MARKER_RESPONSE 0x2255ACACL -#define S2255_RESPONSE_SETMODE 0x01 -#define S2255_RESPONSE_FW 0x10 +#define S2255_VIDSTATUS_TIMEOUT 350 +#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL) +#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL) +#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01) +#define S2255_RESPONSE_FW cpu_to_le32(0x10) +#define S2255_RESPONSE_STATUS cpu_to_le32(0x20) #define S2255_USB_XFER_SIZE (16 * 1024) #define MAX_CHANNELS 4 -#define MAX_PIPE_BUFFERS 1 #define SYS_FRAMES 4 /* maximum size is PAL full size plus room for the marker header(s) */ #define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) @@ -118,9 +123,10 @@ #define COLOR_YUVPK 2 /* YUV packed */ #define COLOR_Y8 4 /* monochrome */ #define COLOR_JPG 5 /* JPEG */ -#define MASK_COLOR 0xff -#define MASK_JPG_QUALITY 0xff00 +#define MASK_COLOR 0x000000ff +#define MASK_JPG_QUALITY 0x0000ff00 +#define MASK_INPUT_TYPE 0x000f0000 /* frame decimation. Not implemented by V4L yet(experimental in V4L) */ #define FDEC_1 1 /* capture every frame. default */ #define FDEC_2 2 /* capture every 2nd frame */ @@ -139,12 +145,12 @@ #define DEF_HUE 0 /* usb config commands */ -#define IN_DATA_TOKEN 0x2255c0de -#define CMD_2255 0xc2255000 -#define CMD_SET_MODE (CMD_2255 | 0x10) -#define CMD_START (CMD_2255 | 0x20) -#define CMD_STOP (CMD_2255 | 0x30) -#define CMD_STATUS (CMD_2255 | 0x40) +#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) +#define CMD_2255 cpu_to_le32(0xc2255000) +#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) +#define CMD_START cpu_to_le32((CMD_2255 | 0x20)) +#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) +#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40)) struct s2255_mode { u32 format; /* input video format (NTSC, PAL) */ @@ -194,7 +200,6 @@ struct s2255_dmaqueue { #define S2255_FW_SUCCESS 2 #define S2255_FW_FAILED 3 #define S2255_FW_DISCONNECTING 4 - #define S2255_FW_MARKER cpu_to_le32(0x22552f2f) /* 2255 read states */ #define S2255_READ_IDLE 0 @@ -223,8 +228,10 @@ struct s2255_pipeinfo { struct s2255_fmt; /*forward declaration */ struct s2255_dev { + struct video_device vdev[MAX_CHANNELS]; + struct v4l2_device v4l2_dev; + atomic_t channels; /* number of channels registered */ int frames; - int users[MAX_CHANNELS]; struct mutex lock; struct mutex open_lock; int resources[MAX_CHANNELS]; @@ -233,11 +240,10 @@ struct s2255_dev { u8 read_endpoint; struct s2255_dmaqueue vidq[MAX_CHANNELS]; - struct video_device *vdev[MAX_CHANNELS]; struct timer_list timer; struct s2255_fw *fw_data; - struct s2255_pipeinfo pipes[MAX_PIPE_BUFFERS]; - struct s2255_bufferi buffer[MAX_CHANNELS]; + struct s2255_pipeinfo pipe; + struct s2255_bufferi buffer[MAX_CHANNELS]; struct s2255_mode mode[MAX_CHANNELS]; /* jpeg compression */ struct v4l2_jpegcompression jc[MAX_CHANNELS]; @@ -261,11 +267,21 @@ struct s2255_dev { int chn_configured[MAX_CHANNELS]; wait_queue_head_t wait_setmode[MAX_CHANNELS]; int setmode_ready[MAX_CHANNELS]; + /* video status items */ + int vidstatus[MAX_CHANNELS]; + wait_queue_head_t wait_vidstatus[MAX_CHANNELS]; + int vidstatus_ready[MAX_CHANNELS]; int chn_ready; - struct kref kref; spinlock_t slock; + /* dsp firmware version (f2255usb.bin) */ + int dsp_fw_ver; + u16 pid; /* product id */ }; -#define to_s2255_dev(d) container_of(d, struct s2255_dev, kref) + +static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct s2255_dev, v4l2_dev); +} struct s2255_fmt { char *name; @@ -296,17 +312,43 @@ struct s2255_fh { /* current cypress EEPROM firmware version */ #define S2255_CUR_USB_FWVER ((3 << 8) | 6) -#define S2255_MAJOR_VERSION 1 -#define S2255_MINOR_VERSION 14 -#define S2255_RELEASE 0 -#define S2255_VERSION KERNEL_VERSION(S2255_MAJOR_VERSION, \ - S2255_MINOR_VERSION, \ - S2255_RELEASE) - -/* vendor ids */ -#define USB_S2255_VENDOR_ID 0x1943 -#define USB_S2255_PRODUCT_ID 0x2255 +/* current DSP FW version */ +#define S2255_CUR_DSP_FWVER 8 +/* Need DSP version 5+ for video status feature */ +#define S2255_MIN_DSP_STATUS 5 +#define S2255_MIN_DSP_COLORFILTER 8 #define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC) + +/* private V4L2 controls */ + +/* + * The following chart displays how COLORFILTER should be set + * ========================================================= + * = fourcc = COLORFILTER = + * = =============================== + * = = 0 = 1 = + * ========================================================= + * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome= + * = = s-video or = composite = + * = = B/W camera = input = + * ========================================================= + * = other = color, svideo = color, = + * = = = composite = + * ========================================================= + * + * Notes: + * channels 0-3 on 2255 are composite + * channels 0-1 on 2257 are composite, 2-3 are s-video + * If COLORFILTER is 0 with a composite color camera connected, + * the output will appear monochrome but hatching + * will occur. + * COLORFILTER is different from "color killer" and "color effects" + * for reasons above. + */ +#define S2255_V4L2_YC_ON 1 +#define S2255_V4L2_YC_OFF 0 +#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0) + /* frame prefix size (sent once every frame) */ #define PREFIX_SIZE 512 @@ -325,9 +367,8 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, struct s2255_mode *mode); static int s2255_board_shutdown(struct s2255_dev *dev); -static void s2255_exit_v4l(struct s2255_dev *dev); static void s2255_fwload_start(struct s2255_dev *dev, int reset); -static void s2255_destroy(struct kref *kref); +static void s2255_destroy(struct s2255_dev *dev); static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, u16 index, u16 value, void *buf, s32 buf_len, int bOut); @@ -347,7 +388,6 @@ static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, static struct usb_driver s2255_driver; - /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ @@ -362,58 +402,16 @@ module_param(video_nr, int, 0644); MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); /* USB device table */ +#define USB_SENSORAY_VID 0x1943 static struct usb_device_id s2255_table[] = { - {USB_DEVICE(USB_S2255_VENDOR_ID, USB_S2255_PRODUCT_ID)}, + {USB_DEVICE(USB_SENSORAY_VID, 0x2255)}, + {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, s2255_table); - #define BUFFER_TIMEOUT msecs_to_jiffies(400) -/* supported controls */ -static struct v4l2_queryctrl s2255_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = -127, - .maximum = 128, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = DEF_CONTRAST, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = DEF_SATURATION, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = DEF_HUE, - .flags = 0, - } -}; - -static int qctl_regs[ARRAY_SIZE(s2255_qctrl)]; - /* image formats. */ static const struct s2255_fmt formats[] = { { @@ -505,7 +503,7 @@ static void s2255_reset_dsppower(struct s2255_dev *dev) static void s2255_timer(unsigned long user_data) { struct s2255_fw *data = (struct s2255_fw *)user_data; - dprintk(100, "s2255 timer\n"); + dprintk(100, "%s\n", __func__); if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { printk(KERN_ERR "s2255: can't submit urb\n"); atomic_set(&data->fw_state, S2255_FW_FAILED); @@ -527,7 +525,7 @@ static void s2255_fwchunk_complete(struct urb *urb) struct s2255_fw *data = urb->context; struct usb_device *udev = urb->dev; int len; - dprintk(100, "udev %p urb %p", udev, urb); + dprintk(100, "%s: udev %p urb %p", __func__, udev, urb); if (urb->status) { dev_err(&udev->dev, "URB failed with status %d\n", urb->status); atomic_set(&data->fw_state, S2255_FW_FAILED); @@ -573,8 +571,8 @@ static void s2255_fwchunk_complete(struct urb *urb) data->fw_loaded += len; } else { atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); + dprintk(100, "%s: firmware upload complete\n", __func__); } - dprintk(100, "2255 complete done\n"); return; } @@ -585,9 +583,7 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) struct s2255_buffer *buf; unsigned long flags = 0; int rc = 0; - dprintk(2, "wakeup: %p channel: %d\n", &dma_q, chn); spin_lock_irqsave(&dev->slock, flags); - if (list_empty(&dma_q->active)) { dprintk(1, "No active queue to serve\n"); rc = -1; @@ -595,23 +591,19 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) } buf = list_entry(dma_q->active.next, struct s2255_buffer, vb.queue); - list_del(&buf->vb.queue); do_gettimeofday(&buf->vb.ts); - dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i); s2255_fillbuff(dev, buf, dma_q->channel, jpgsize); wake_up(&buf->vb.done); - dprintk(2, "wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); + dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); unlock: spin_unlock_irqrestore(&dev->slock, flags); return 0; } - static const struct s2255_fmt *format_by_fourcc(int fourcc) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(formats); i++) { if (-1 == formats[i].fourcc) continue; @@ -621,9 +613,6 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc) return NULL; } - - - /* video buffer vmalloc implementation based partly on VIVI driver which is * Copyright (c) 2006 by * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> @@ -703,8 +692,8 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, if (0 == *count) *count = S2255_DEF_BUFS; - while (*size * (*count) > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } @@ -727,10 +716,10 @@ static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (fh->fmt == NULL) return -EINVAL; - if ((fh->width < norm_minw(fh->dev->vdev[fh->channel])) || - (fh->width > norm_maxw(fh->dev->vdev[fh->channel])) || - (fh->height < norm_minh(fh->dev->vdev[fh->channel])) || - (fh->height > norm_maxh(fh->dev->vdev[fh->channel]))) { + if ((fh->width < norm_minw(&fh->dev->vdev[fh->channel])) || + (fh->width > norm_maxw(&fh->dev->vdev[fh->channel])) || + (fh->height < norm_minh(&fh->dev->vdev[fh->channel])) || + (fh->height > norm_maxh(&fh->dev->vdev[fh->channel]))) { dprintk(4, "invalid buffer prepare\n"); return -EINVAL; } @@ -747,7 +736,6 @@ static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, buf->vb.height = fh->height; buf->vb.field = field; - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { rc = videobuf_iolock(vq, &buf->vb, NULL); if (rc < 0) @@ -767,9 +755,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) struct s2255_fh *fh = vq->priv_data; struct s2255_dev *dev = fh->dev; struct s2255_dmaqueue *vidq = &dev->vidq[fh->channel]; - dprintk(1, "%s\n", __func__); - buf->vb.state = VIDEOBUF_QUEUED; list_add_tail(&buf->vb.queue, &vidq->active); } @@ -828,6 +814,27 @@ static void res_free(struct s2255_dev *dev, struct s2255_fh *fh) dprintk(1, "res: put\n"); } +static int vidioc_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *qmenu) +{ + static const char *colorfilter[] = { + "Off", + "On", + NULL + }; + if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) { + int i; + const char **menu_items = colorfilter; + for (i = 0; i < qmenu->index && menu_items[i]; i++) + ; /* do nothing (from v4l2-common.c) */ + if (menu_items[i] == NULL || menu_items[i][0] == '\0') + return -EINVAL; + strlcpy(qmenu->name, menu_items[qmenu->index], + sizeof(qmenu->name)); + return 0; + } + return v4l2_ctrl_query_menu(qmenu, NULL, NULL); +} static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) @@ -883,7 +890,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, int is_ntsc; is_ntsc = - (dev->vdev[fh->channel]->current_norm & V4L2_STD_NTSC) ? 1 : 0; + (dev->vdev[fh->channel].current_norm & V4L2_STD_NTSC) ? 1 : 0; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -894,10 +901,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, if (field == V4L2_FIELD_ANY) b_any_field = 1; - dprintk(4, "try format %d \n", is_ntsc); - /* supports 3 sizes. see s2255drv.h */ - dprintk(50, "width test %d, height %d\n", - f->fmt.pix.width, f->fmt.pix.height); + dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n", + __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); if (is_ntsc) { /* NTSC */ if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { @@ -952,29 +957,24 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } } if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) { - dprintk(50, "pal 704\n"); f->fmt.pix.width = LINE_SZ_4CIFS_PAL; field = V4L2_FIELD_SEQ_TB; } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) { - dprintk(50, "pal 352A\n"); f->fmt.pix.width = LINE_SZ_2CIFS_PAL; field = V4L2_FIELD_TOP; } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) { - dprintk(50, "pal 352B\n"); f->fmt.pix.width = LINE_SZ_1CIFS_PAL; field = V4L2_FIELD_TOP; } else { - dprintk(50, "pal 352C\n"); f->fmt.pix.width = LINE_SZ_1CIFS_PAL; field = V4L2_FIELD_TOP; } } - - dprintk(50, "width %d height %d field %d \n", f->fmt.pix.width, - f->fmt.pix.height, f->fmt.pix.field); f->fmt.pix.field = field; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + dprintk(50, "%s: set width %d height %d field %d\n", __func__, + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); return 0; } @@ -1006,7 +1006,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, } if (res_locked(fh->dev, fh)) { - dprintk(1, "can't change format after started\n"); + dprintk(1, "%s: channel busy\n", __func__); ret = -EBUSY; goto out_s_fmt; } @@ -1016,17 +1016,14 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - norm = norm_minw(fh->dev->vdev[fh->channel]); - if (fh->width > norm_minw(fh->dev->vdev[fh->channel])) { - if (fh->height > norm_minh(fh->dev->vdev[fh->channel])) { + norm = norm_minw(&fh->dev->vdev[fh->channel]); + if (fh->width > norm_minw(&fh->dev->vdev[fh->channel])) { + if (fh->height > norm_minh(&fh->dev->vdev[fh->channel])) { if (fh->dev->cap_parm[fh->channel].capturemode & - V4L2_MODE_HIGHQUALITY) { + V4L2_MODE_HIGHQUALITY) fh->mode.scale = SCALE_4CIFSI; - dprintk(2, "scale 4CIFSI\n"); - } else { + else fh->mode.scale = SCALE_4CIFS; - dprintk(2, "scale 4CIFS\n"); - } } else fh->mode.scale = SCALE_2CIFS; @@ -1037,19 +1034,23 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, /* color mode */ switch (fh->fmt->fourcc) { case V4L2_PIX_FMT_GREY: - fh->mode.color = COLOR_Y8; + fh->mode.color &= ~MASK_COLOR; + fh->mode.color |= COLOR_Y8; break; case V4L2_PIX_FMT_JPEG: - fh->mode.color = COLOR_JPG | - (fh->dev->jc[fh->channel].quality << 8); + fh->mode.color &= ~MASK_COLOR; + fh->mode.color |= COLOR_JPG; + fh->mode.color |= (fh->dev->jc[fh->channel].quality << 8); break; case V4L2_PIX_FMT_YUV422P: - fh->mode.color = COLOR_YUVPL; + fh->mode.color &= ~MASK_COLOR; + fh->mode.color |= COLOR_YUVPL; break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: default: - fh->mode.color = COLOR_YUVPK; + fh->mode.color &= ~MASK_COLOR; + fh->mode.color |= COLOR_YUVPK; break; } ret = 0; @@ -1178,19 +1179,13 @@ static u32 get_transfer_size(struct s2255_mode *mode) return usbInSize; } -static void dump_verify_mode(struct s2255_dev *sdev, struct s2255_mode *mode) +static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) { struct device *dev = &sdev->udev->dev; dev_info(dev, "------------------------------------------------\n"); - dev_info(dev, "verify mode\n"); - dev_info(dev, "format: %d\n", mode->format); - dev_info(dev, "scale: %d\n", mode->scale); - dev_info(dev, "fdec: %d\n", mode->fdec); - dev_info(dev, "color: %d\n", mode->color); + dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); + dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); dev_info(dev, "bright: 0x%x\n", mode->bright); - dev_info(dev, "restart: 0x%x\n", mode->restart); - dev_info(dev, "usb_block: 0x%x\n", mode->usb_block); - dev_info(dev, "single: 0x%x\n", mode->single); dev_info(dev, "------------------------------------------------\n"); } @@ -1206,44 +1201,38 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, struct s2255_mode *mode) { int res; - u32 *buffer; + __le32 *buffer; unsigned long chn_rev; - mutex_lock(&dev->lock); chn_rev = G_chnmap[chn]; - dprintk(3, "mode scale [%ld] %p %d\n", chn, mode, mode->scale); - dprintk(3, "mode scale [%ld] %p %d\n", chn, &dev->mode[chn], - dev->mode[chn].scale); - dprintk(2, "mode contrast %x\n", mode->contrast); - + dprintk(3, "%s channel %lu\n", __func__, chn); /* if JPEG, set the quality */ - if ((mode->color & MASK_COLOR) == COLOR_JPG) - mode->color = (dev->jc[chn].quality << 8) | COLOR_JPG; - + if ((mode->color & MASK_COLOR) == COLOR_JPG) { + mode->color &= ~MASK_COLOR; + mode->color |= COLOR_JPG; + mode->color &= ~MASK_JPG_QUALITY; + mode->color |= (dev->jc[chn].quality << 8); + } /* save the mode */ dev->mode[chn] = *mode; dev->req_image_size[chn] = get_transfer_size(mode); - dprintk(1, "transfer size %ld\n", dev->req_image_size[chn]); - + dprintk(1, "%s: reqsize %ld\n", __func__, dev->req_image_size[chn]); buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); mutex_unlock(&dev->lock); return -ENOMEM; } - /* set the mode */ buffer[0] = IN_DATA_TOKEN; - buffer[1] = (u32) chn_rev; + buffer[1] = (__le32) cpu_to_le32(chn_rev); buffer[2] = CMD_SET_MODE; memcpy(&buffer[3], &dev->mode[chn], sizeof(struct s2255_mode)); dev->setmode_ready[chn] = 0; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (debug) - dump_verify_mode(dev, mode); + s2255_print_cfg(dev, mode); kfree(buffer); - dprintk(1, "set mode done chn %lu, %d\n", chn, res); - /* wait at least 3 frames before continuing */ if (mode->restart) { wait_event_timeout(dev->wait_setmode[chn], @@ -1254,10 +1243,46 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, res = -EFAULT; } } - /* clear the restart flag */ dev->mode[chn].restart = 0; mutex_unlock(&dev->lock); + dprintk(1, "%s chn %lu, result: %d\n", __func__, chn, res); + return res; +} + +static int s2255_cmd_status(struct s2255_dev *dev, unsigned long chn, + u32 *pstatus) +{ + int res; + __le32 *buffer; + u32 chn_rev; + mutex_lock(&dev->lock); + chn_rev = G_chnmap[chn]; + dprintk(4, "%s chan %lu\n", __func__, chn); + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + mutex_unlock(&dev->lock); + return -ENOMEM; + } + /* form the get vid status command */ + buffer[0] = IN_DATA_TOKEN; + buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[2] = CMD_STATUS; + *pstatus = 0; + dev->vidstatus_ready[chn] = 0; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + kfree(buffer); + wait_event_timeout(dev->wait_vidstatus[chn], + (dev->vidstatus_ready[chn] != 0), + msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); + if (dev->vidstatus_ready[chn] != 1) { + printk(KERN_DEBUG "s2255: no vidstatus response\n"); + res = -EFAULT; + } + *pstatus = dev->vidstatus[chn]; + dprintk(4, "%s, vid status %d\n", __func__, *pstatus); + mutex_unlock(&dev->lock); return res; } @@ -1291,7 +1316,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) new_mode = &fh->mode; old_mode = &fh->dev->mode[chn]; - if (new_mode->color != old_mode->color) + if ((new_mode->color & MASK_COLOR) != (old_mode->color & MASK_COLOR)) new_mode->restart = 1; else if (new_mode->scale != old_mode->scale) new_mode->restart = 1; @@ -1302,7 +1327,6 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) new_mode->restart = 0; *old_mode = *new_mode; dev->cur_fmt[chn] = fh->fmt; - dprintk(1, "%s[%d]\n", __func__, chn); dev->last_frame[chn] = -1; dev->bad_payload[chn] = 0; dev->cur_frame[chn] = 0; @@ -1325,7 +1349,6 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; - dprintk(4, "%s\n, channel: %d", __func__, fh->channel); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk(KERN_ERR "invalid fh type0\n"); @@ -1347,27 +1370,32 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) struct s2255_mode *mode; struct videobuf_queue *q = &fh->vb_vidq; int ret = 0; - mutex_lock(&q->vb_lock); if (videobuf_queue_is_busy(q)) { dprintk(1, "queue busy\n"); ret = -EBUSY; goto out_s_std; } - if (res_locked(fh->dev, fh)) { dprintk(1, "can't change standard after started\n"); ret = -EBUSY; goto out_s_std; } mode = &fh->mode; - if (*i & V4L2_STD_NTSC) { - dprintk(4, "vidioc_s_std NTSC\n"); - mode->format = FORMAT_NTSC; + dprintk(4, "%s NTSC\n", __func__); + /* if changing format, reset frame decimation/intervals */ + if (mode->format != FORMAT_NTSC) { + mode->format = FORMAT_NTSC; + mode->fdec = FDEC_1; + } } else if (*i & V4L2_STD_PAL) { - dprintk(4, "vidioc_s_std PAL\n"); + dprintk(4, "%s PAL\n", __func__); mode->format = FORMAT_PAL; + if (mode->format != FORMAT_PAL) { + mode->format = FORMAT_PAL; + mode->fdec = FDEC_1; + } } else { ret = -EINVAL; } @@ -1386,12 +1414,32 @@ out_s_std: static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + u32 status = 0; if (inp->index != 0) return -EINVAL; - inp->type = V4L2_INPUT_TYPE_CAMERA; inp->std = S2255_NORMS; - strlcpy(inp->name, "Camera", sizeof(inp->name)); + inp->status = 0; + if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { + int rc; + rc = s2255_cmd_status(dev, fh->channel, &status); + dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status); + if (rc == 0) + inp->status = (status & 0x01) ? 0 + : V4L2_IN_ST_NO_SIGNAL; + } + switch (dev->pid) { + case 0x2255: + default: + strlcpy(inp->name, "Composite", sizeof(inp->name)); + break; + case 0x2257: + strlcpy(inp->name, (fh->channel < 2) ? "Composite" : "S-Video", + sizeof(inp->name)); + break; + } return 0; } @@ -1411,74 +1459,113 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { - int i; - - for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) - if (qc->id && qc->id == s2255_qctrl[i].id) { - memcpy(qc, &(s2255_qctrl[i]), sizeof(*qc)); - return 0; - } - - dprintk(4, "query_ctrl -EINVAL %d\n", qc->id); - return -EINVAL; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT); + break; + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST); + break; + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION); + break; + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE); + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (fh->channel > 1)) + return -EINVAL; + strlcpy(qc->name, "Color Filter", sizeof(qc->name)); + qc->type = V4L2_CTRL_TYPE_MENU; + qc->minimum = 0; + qc->maximum = 1; + qc->step = 1; + qc->default_value = 1; + qc->flags = 0; + break; + default: + return -EINVAL; + } + dprintk(4, "%s, id %d\n", __func__, qc->id); + return 0; } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - int i; - - for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) - if (ctrl->id == s2255_qctrl[i].id) { - ctrl->value = qctl_regs[i]; - return 0; - } - dprintk(4, "g_ctrl -EINVAL\n"); - - return -EINVAL; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = fh->mode.bright; + break; + case V4L2_CID_CONTRAST: + ctrl->value = fh->mode.contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = fh->mode.saturation; + break; + case V4L2_CID_HUE: + ctrl->value = fh->mode.hue; + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (fh->channel > 1)) + return -EINVAL; + ctrl->value = !((fh->mode.color & MASK_INPUT_TYPE) >> 16); + break; + default: + return -EINVAL; + } + dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value); + return 0; } static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - int i; struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; struct s2255_mode *mode; mode = &fh->mode; - dprintk(4, "vidioc_s_ctrl\n"); - for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) { - if (ctrl->id == s2255_qctrl[i].id) { - if (ctrl->value < s2255_qctrl[i].minimum || - ctrl->value > s2255_qctrl[i].maximum) - return -ERANGE; - - qctl_regs[i] = ctrl->value; - /* update the mode to the corresponding value */ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - mode->bright = ctrl->value; - break; - case V4L2_CID_CONTRAST: - mode->contrast = ctrl->value; - break; - case V4L2_CID_HUE: - mode->hue = ctrl->value; - break; - case V4L2_CID_SATURATION: - mode->saturation = ctrl->value; - break; - } - mode->restart = 0; - /* set mode here. Note: stream does not need restarted. - some V4L programs restart stream unnecessarily - after a s_crtl. - */ - s2255_set_mode(dev, fh->channel, mode); - return 0; - } + dprintk(4, "%s\n", __func__); + /* update the mode to the corresponding value */ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + mode->bright = ctrl->value; + break; + case V4L2_CID_CONTRAST: + mode->contrast = ctrl->value; + break; + case V4L2_CID_HUE: + mode->hue = ctrl->value; + break; + case V4L2_CID_SATURATION: + mode->saturation = ctrl->value; + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (fh->channel > 1)) + return -EINVAL; + mode->color &= ~MASK_INPUT_TYPE; + mode->color |= ((ctrl->value ? 0 : 1) << 16); + break; + default: + return -EINVAL; } - return -EINVAL; + mode->restart = 0; + /* set mode here. Note: stream does not need restarted. + some V4L programs restart stream unnecessarily + after a s_crtl. + */ + s2255_set_mode(dev, fh->channel, mode); + return 0; } static int vidioc_g_jpegcomp(struct file *file, void *priv, @@ -1487,7 +1574,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; *jc = dev->jc[fh->channel]; - dprintk(2, "getting jpegcompression, quality %d\n", jc->quality); + dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1499,7 +1586,7 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, if (jc->quality < 0 || jc->quality > 100) return -EINVAL; dev->jc[fh->channel].quality = jc->quality; - dprintk(2, "setting jpeg quality %d\n", jc->quality); + dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1508,10 +1595,34 @@ static int vidioc_g_parm(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; + __u32 def_num, def_dem; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + memset(sp, 0, sizeof(struct v4l2_streamparm)); + sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; sp->parm.capture.capturemode = dev->cap_parm[fh->channel].capturemode; - dprintk(2, "getting parm %d\n", sp->parm.capture.capturemode); + def_num = (fh->mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (fh->mode.format == FORMAT_NTSC) ? 30000 : 25000; + sp->parm.capture.timeperframe.denominator = def_dem; + switch (fh->mode.fdec) { + default: + case FDEC_1: + sp->parm.capture.timeperframe.numerator = def_num; + break; + case FDEC_2: + sp->parm.capture.timeperframe.numerator = def_num * 2; + break; + case FDEC_3: + sp->parm.capture.timeperframe.numerator = def_num * 3; + break; + case FDEC_5: + sp->parm.capture.timeperframe.numerator = def_num * 5; + break; + } + dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__, + sp->parm.capture.capturemode, + sp->parm.capture.timeperframe.numerator, + sp->parm.capture.timeperframe.denominator); return 0; } @@ -1520,15 +1631,79 @@ static int vidioc_s_parm(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; - + int fdec = FDEC_1; + __u32 def_num, def_dem; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + /* high quality capture mode requires a stream restart */ + if (dev->cap_parm[fh->channel].capturemode + != sp->parm.capture.capturemode && res_locked(fh->dev, fh)) + return -EBUSY; + def_num = (fh->mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (fh->mode.format == FORMAT_NTSC) ? 30000 : 25000; + if (def_dem != sp->parm.capture.timeperframe.denominator) + sp->parm.capture.timeperframe.numerator = def_num; + else if (sp->parm.capture.timeperframe.numerator <= def_num) + sp->parm.capture.timeperframe.numerator = def_num; + else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { + sp->parm.capture.timeperframe.numerator = def_num * 2; + fdec = FDEC_2; + } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { + sp->parm.capture.timeperframe.numerator = def_num * 3; + fdec = FDEC_3; + } else { + sp->parm.capture.timeperframe.numerator = def_num * 5; + fdec = FDEC_5; + } + fh->mode.fdec = fdec; + sp->parm.capture.timeperframe.denominator = def_dem; + s2255_set_mode(dev, fh->channel, &fh->mode); + dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", + __func__, + sp->parm.capture.capturemode, + sp->parm.capture.timeperframe.numerator, + sp->parm.capture.timeperframe.denominator, fdec); + return 0; +} - dev->cap_parm[fh->channel].capturemode = sp->parm.capture.capturemode; - dprintk(2, "setting param capture mode %d\n", - sp->parm.capture.capturemode); +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fe) +{ + int is_ntsc = 0; +#define NUM_FRAME_ENUMS 4 + int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; + if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS) + return -EINVAL; + switch (fe->width) { + case 640: + if (fe->height != 240 && fe->height != 480) + return -EINVAL; + is_ntsc = 1; + break; + case 320: + if (fe->height != 240) + return -EINVAL; + is_ntsc = 1; + break; + case 704: + if (fe->height != 288 && fe->height != 576) + return -EINVAL; + break; + case 352: + if (fe->height != 288) + return -EINVAL; + break; + default: + return -EINVAL; + } + fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fe->discrete.denominator = is_ntsc ? 30000 : 25000; + fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; + dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator, + fe->discrete.denominator); return 0; } + static int s2255_open(struct file *file) { struct video_device *vdev = video_devdata(file); @@ -1538,31 +1713,29 @@ static int s2255_open(struct file *file) int i = 0; int cur_channel = -1; int state; - dprintk(1, "s2255: open called (dev=%s)\n", video_device_node_name(vdev)); - lock_kernel(); - for (i = 0; i < MAX_CHANNELS; i++) { - if (dev->vdev[i] == vdev) { + if (&dev->vdev[i] == vdev) { cur_channel = i; break; } } - - if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING) { - unlock_kernel(); - printk(KERN_INFO "disconnecting\n"); + if (i == MAX_CHANNELS) return -ENODEV; - } - kref_get(&dev->kref); - mutex_lock(&dev->open_lock); - - dev->users[cur_channel]++; - dprintk(4, "s2255: open_handles %d\n", dev->users[cur_channel]); - switch (atomic_read(&dev->fw_data->fw_state)) { + /* + * open lock necessary to prevent multiple instances + * of v4l-conf (or other programs) from simultaneously + * reloading firmware. + */ + mutex_lock(&dev->open_lock); + state = atomic_read(&dev->fw_data->fw_state); + switch (state) { + case S2255_FW_DISCONNECTING: + mutex_unlock(&dev->open_lock); + return -ENODEV; case S2255_FW_FAILED: s2255_dev_err(&dev->udev->dev, "firmware load failed. retrying.\n"); @@ -1573,6 +1746,8 @@ static int s2255_open(struct file *file) (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING)), msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + /* state may have changed, re-read */ + state = atomic_read(&dev->fw_data->fw_state); break; case S2255_FW_NOTLOADED: case S2255_FW_LOADED_DSPWAIT: @@ -1584,53 +1759,50 @@ static int s2255_open(struct file *file) == S2255_FW_SUCCESS) || (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING)), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + /* state may have changed, re-read */ + state = atomic_read(&dev->fw_data->fw_state); break; case S2255_FW_SUCCESS: default: break; } - state = atomic_read(&dev->fw_data->fw_state); - if (state != S2255_FW_SUCCESS) { - int rc; - switch (state) { - case S2255_FW_FAILED: - printk(KERN_INFO "2255 FW load failed. %d\n", state); - rc = -ENODEV; - break; - case S2255_FW_DISCONNECTING: - printk(KERN_INFO "%s: disconnecting\n", __func__); - rc = -ENODEV; - break; - case S2255_FW_LOADED_DSPWAIT: - case S2255_FW_NOTLOADED: - printk(KERN_INFO "%s: firmware not loaded yet" - "please try again later\n", - __func__); - rc = -EAGAIN; - break; - default: - printk(KERN_INFO "%s: unknown state\n", __func__); - rc = -EFAULT; - break; - } - dev->users[cur_channel]--; + /* state may have changed in above switch statement */ + switch (state) { + case S2255_FW_SUCCESS: + break; + case S2255_FW_FAILED: + printk(KERN_INFO "2255 firmware load failed.\n"); + mutex_unlock(&dev->open_lock); + return -ENODEV; + case S2255_FW_DISCONNECTING: + printk(KERN_INFO "%s: disconnecting\n", __func__); mutex_unlock(&dev->open_lock); - kref_put(&dev->kref, s2255_destroy); - unlock_kernel(); - return rc; + return -ENODEV; + case S2255_FW_LOADED_DSPWAIT: + case S2255_FW_NOTLOADED: + printk(KERN_INFO "%s: firmware not loaded yet" + "please try again later\n", + __func__); + /* + * Timeout on firmware load means device unusable. + * Set firmware failure state. + * On next s2255_open the firmware will be reloaded. + */ + atomic_set(&dev->fw_data->fw_state, + S2255_FW_FAILED); + mutex_unlock(&dev->open_lock); + return -EAGAIN; + default: + printk(KERN_INFO "%s: unknown state\n", __func__); + mutex_unlock(&dev->open_lock); + return -EFAULT; } - + mutex_unlock(&dev->open_lock); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) { - dev->users[cur_channel]--; - mutex_unlock(&dev->open_lock); - kref_put(&dev->kref, s2255_destroy); - unlock_kernel(); + if (NULL == fh) return -ENOMEM; - } - file->private_data = fh; fh->dev = dev; fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1640,35 +1812,23 @@ static int s2255_open(struct file *file) fh->width = LINE_SZ_4CIFS_NTSC; fh->height = NUM_LINES_4CIFS_NTSC * 2; fh->channel = cur_channel; - /* configure channel to default state */ if (!dev->chn_configured[cur_channel]) { s2255_set_mode(dev, cur_channel, &fh->mode); dev->chn_configured[cur_channel] = 1; } - - - /* Put all controls at a sane state */ - for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) - qctl_regs[i] = s2255_qctrl[i].default_value; - - dprintk(1, "s2255drv: open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[type], - dev->users[cur_channel]); - dprintk(2, "s2255drv: open: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", + dprintk(1, "%s: dev=%s type=%s\n", __func__, + video_device_node_name(vdev), v4l2_type_names[type]); + dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__, (unsigned long)fh, (unsigned long)dev, (unsigned long)&dev->vidq[cur_channel]); - dprintk(4, "s2255drv: open: list_empty active=%d\n", + dprintk(4, "%s: list_empty active=%d\n", __func__, list_empty(&dev->vidq[cur_channel].active)); - videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct s2255_buffer), fh); - - mutex_unlock(&dev->open_lock); - unlock_kernel(); return 0; } @@ -1679,39 +1839,19 @@ static unsigned int s2255_poll(struct file *file, struct s2255_fh *fh = file->private_data; int rc; dprintk(100, "%s\n", __func__); - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) return POLLERR; - rc = videobuf_poll_stream(file, &fh->vb_vidq, wait); return rc; } -static void s2255_destroy(struct kref *kref) +static void s2255_destroy(struct s2255_dev *dev) { - struct s2255_dev *dev = to_s2255_dev(kref); - int i; - if (!dev) { - printk(KERN_ERR "s2255drv: kref problem\n"); - return; - } - atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); - wake_up(&dev->fw_data->wait_fw); - for (i = 0; i < MAX_CHANNELS; i++) { - dev->setmode_ready[i] = 1; - wake_up(&dev->wait_setmode[i]); - } - mutex_lock(&dev->open_lock); - /* reset the DSP so firmware can be reload next time */ - s2255_reset_dsppower(dev); - s2255_exit_v4l(dev); /* board shutdown stops the read pipe if it is running */ s2255_board_shutdown(dev); /* make sure firmware still not trying to load */ del_timer(&dev->timer); /* only started in .probe and .open */ - if (dev->fw_data->fw_urb) { - dprintk(2, "kill fw_urb\n"); usb_kill_urb(dev->fw_data->fw_urb); usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; @@ -1720,24 +1860,22 @@ static void s2255_destroy(struct kref *kref) release_firmware(dev->fw_data->fw); kfree(dev->fw_data->pfw_data); kfree(dev->fw_data); + /* reset the DSP so firmware can be reloaded next time */ + s2255_reset_dsppower(dev); + mutex_destroy(&dev->open_lock); + mutex_destroy(&dev->lock); usb_put_dev(dev->udev); dprintk(1, "%s", __func__); - - mutex_unlock(&dev->open_lock); kfree(dev); } -static int s2255_close(struct file *file) +static int s2255_release(struct file *file) { struct s2255_fh *fh = file->private_data; struct s2255_dev *dev = fh->dev; struct video_device *vdev = video_devdata(file); - if (!dev) return -ENODEV; - - mutex_lock(&dev->open_lock); - /* turn off stream */ if (res_check(fh)) { if (dev->b_acquire[fh->channel]) @@ -1745,15 +1883,8 @@ static int s2255_close(struct file *file) videobuf_streamoff(&fh->vb_vidq); res_free(dev, fh); } - videobuf_mmap_free(&fh->vb_vidq); - dev->users[fh->channel]--; - - mutex_unlock(&dev->open_lock); - - kref_put(&dev->kref, s2255_destroy); - dprintk(1, "s2255: close called (dev=%s, users=%d)\n", - video_device_node_name(vdev), dev->users[fh->channel]); + dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); kfree(fh); return 0; } @@ -1765,27 +1896,25 @@ static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma) if (!fh) return -ENODEV; - dprintk(4, "mmap called, vma=0x%08lx\n", (unsigned long)vma); - + dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma); ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); - - dprintk(4, "vma start=0x%08lx, size=%ld, ret=%d\n", + dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__, (unsigned long)vma->vm_start, (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; } static const struct v4l2_file_operations s2255_fops_v4l = { .owner = THIS_MODULE, .open = s2255_open, - .release = s2255_close, + .release = s2255_release, .poll = s2255_poll, .ioctl = video_ioctl2, /* V4L2 ioctl handler */ .mmap = s2255_mmap_v4l, }; static const struct v4l2_ioctl_ops s2255_ioctl_ops = { + .vidioc_querymenu = vidioc_querymenu, .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1811,13 +1940,23 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { .vidioc_g_jpegcomp = vidioc_g_jpegcomp, .vidioc_s_parm = vidioc_s_parm, .vidioc_g_parm = vidioc_g_parm, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, }; +static void s2255_video_device_release(struct video_device *vdev) +{ + struct s2255_dev *dev = video_get_drvdata(vdev); + dprintk(4, "%s, chnls: %d \n", __func__, atomic_read(&dev->channels)); + if (atomic_dec_and_test(&dev->channels)) + s2255_destroy(dev); + return; +} + static struct video_device template = { .name = "s2255v", .fops = &s2255_fops_v4l, .ioctl_ops = &s2255_ioctl_ops, - .release = video_device_release, + .release = s2255_video_device_release, .tvnorms = S2255_NORMS, .current_norm = V4L2_STD_NTSC_M, }; @@ -1827,7 +1966,9 @@ static int s2255_probe_v4l(struct s2255_dev *dev) int ret; int i; int cur_nr = video_nr; - + ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); + if (ret) + return ret; /* initialize all video 4 linux */ /* register 4 video devices */ for (i = 0; i < MAX_CHANNELS; i++) { @@ -1835,45 +1976,39 @@ static int s2255_probe_v4l(struct s2255_dev *dev) dev->vidq[i].dev = dev; dev->vidq[i].channel = i; /* register 4 video devices */ - dev->vdev[i] = video_device_alloc(); - memcpy(dev->vdev[i], &template, sizeof(struct video_device)); - dev->vdev[i]->parent = &dev->interface->dev; - video_set_drvdata(dev->vdev[i], dev); + memcpy(&dev->vdev[i], &template, sizeof(struct video_device)); + dev->vdev[i].v4l2_dev = &dev->v4l2_dev; + video_set_drvdata(&dev->vdev[i], dev); if (video_nr == -1) - ret = video_register_device(dev->vdev[i], + ret = video_register_device(&dev->vdev[i], VFL_TYPE_GRABBER, video_nr); else - ret = video_register_device(dev->vdev[i], + ret = video_register_device(&dev->vdev[i], VFL_TYPE_GRABBER, cur_nr + i); - video_set_drvdata(dev->vdev[i], dev); - - if (ret != 0) { + if (ret) { dev_err(&dev->udev->dev, "failed to register video device!\n"); - return ret; + break; } + atomic_inc(&dev->channels); + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(&dev->vdev[i])); + } + printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %d.%d\n", S2255_MAJOR_VERSION, S2255_MINOR_VERSION); - return ret; -} - -static void s2255_exit_v4l(struct s2255_dev *dev) -{ - - int i; - for (i = 0; i < MAX_CHANNELS; i++) { - if (video_is_registered(dev->vdev[i])) { - video_unregister_device(dev->vdev[i]); - printk(KERN_INFO "s2255 unregistered\n"); - } else { - video_device_release(dev->vdev[i]); - printk(KERN_INFO "s2255 released\n"); - } + /* if no channels registered, return error and probe will fail*/ + if (atomic_read(&dev->channels) == 0) { + v4l2_device_unregister(&dev->v4l2_dev); + return ret; } + if (atomic_read(&dev->channels) != MAX_CHANNELS) + printk(KERN_WARNING "s2255: Not all channels available.\n"); + return 0; } /* this function moves the usb stream read pipe data @@ -1907,14 +2042,14 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if (frm->ulState == S2255_READ_IDLE) { int jj; unsigned int cc; - s32 *pdword; + __le32 *pdword; /*data from dsp is little endian */ int payload; /* search for marker codes */ pdata = (unsigned char *)pipe_info->transfer_buffer; + pdword = (__le32 *)pdata; for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { - switch (*(s32 *) pdata) { + switch (*pdword) { case S2255_MARKER_FRAME: - pdword = (s32 *)pdata; dprintk(4, "found frame marker at offset:" " %d [%x %x]\n", jj, pdata[0], pdata[1]); @@ -1938,7 +2073,6 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) dev->jpg_size[dev->cc] = pdword[4]; break; case S2255_MARKER_RESPONSE: - pdword = (s32 *)pdata; pdata += DEF_USB_BLOCK; jj += DEF_USB_BLOCK; if (pdword[1] >= MAX_CHANNELS) @@ -1955,7 +2089,6 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) dprintk(5, "setmode ready %d\n", cc); break; case S2255_RESPONSE_FW: - dev->chn_ready |= (1 << cc); if ((dev->chn_ready & 0x0f) != 0x0f) break; @@ -1965,6 +2098,13 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) S2255_FW_SUCCESS); wake_up(&dev->fw_data->wait_fw); break; + case S2255_RESPONSE_STATUS: + dev->vidstatus[cc] = pdword[3]; + dev->vidstatus_ready[cc] = 1; + wake_up(&dev->wait_vidstatus[cc]); + dprintk(5, "got vidstatus %x chan %d\n", + pdword[3], cc); + break; default: printk(KERN_INFO "s2255 unknown resp\n"); } @@ -2165,28 +2305,22 @@ static int s2255_release_sys_buffers(struct s2255_dev *dev, static int s2255_board_init(struct s2255_dev *dev) { - int j; struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; int fw_ver; + int j; + struct s2255_pipeinfo *pipe = &dev->pipe; dprintk(4, "board init: %p", dev); - - for (j = 0; j < MAX_PIPE_BUFFERS; j++) { - struct s2255_pipeinfo *pipe = &dev->pipes[j]; - - memset(pipe, 0, sizeof(*pipe)); - pipe->dev = dev; - pipe->cur_transfer_size = S2255_USB_XFER_SIZE; - pipe->max_transfer_size = S2255_USB_XFER_SIZE; - - pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, - GFP_KERNEL); - if (pipe->transfer_buffer == NULL) { - dprintk(1, "out of memory!\n"); - return -ENOMEM; - } - + memset(pipe, 0, sizeof(*pipe)); + pipe->dev = dev; + pipe->cur_transfer_size = S2255_USB_XFER_SIZE; + pipe->max_transfer_size = S2255_USB_XFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + dprintk(1, "out of memory!\n"); + return -ENOMEM; } - /* query the firmware */ fw_ver = s2255_get_fx2fw(dev); @@ -2203,6 +2337,8 @@ static int s2255_board_init(struct s2255_dev *dev) for (j = 0; j < MAX_CHANNELS; j++) { dev->b_acquire[j] = 0; dev->mode[j] = mode_def; + if (dev->pid == 0x2257 && j > 1) + dev->mode[j].color |= (1 << 16); dev->jc[j].quality = S2255_DEF_JPEG_QUAL; dev->cur_fmt[j] = &formats[0]; dev->mode[j].restart = 1; @@ -2213,16 +2349,14 @@ static int s2255_board_init(struct s2255_dev *dev) } /* start read pipe */ s2255_start_readpipe(dev); - - dprintk(1, "S2255: board initialized\n"); + dprintk(1, "%s: success\n", __func__); return 0; } static int s2255_board_shutdown(struct s2255_dev *dev) { u32 i; - - dprintk(1, "S2255: board shutdown: %p", dev); + dprintk(1, "%s: dev: %p", __func__, dev); for (i = 0; i < MAX_CHANNELS; i++) { if (dev->b_acquire[i]) @@ -2233,12 +2367,8 @@ static int s2255_board_shutdown(struct s2255_dev *dev) for (i = 0; i < MAX_CHANNELS; i++) s2255_release_sys_buffers(dev, i); - - /* release transfer buffers */ - for (i = 0; i < MAX_PIPE_BUFFERS; i++) { - struct s2255_pipeinfo *pipe = &dev->pipes[i]; - kfree(pipe->transfer_buffer); - } + /* release transfer buffer */ + kfree(dev->pipe.transfer_buffer); return 0; } @@ -2248,9 +2378,8 @@ static void read_pipe_completion(struct urb *purb) struct s2255_dev *dev; int status; int pipe; - pipe_info = purb->context; - dprintk(100, "read pipe completion %p, status %d\n", purb, + dprintk(100, "%s: urb:%p, status %d\n", __func__, purb, purb->status); if (pipe_info == NULL) { dev_err(&purb->dev->dev, "no context!\n"); @@ -2265,13 +2394,13 @@ static void read_pipe_completion(struct urb *purb) status = purb->status; /* if shutting down, do not resubmit, exit immediately */ if (status == -ESHUTDOWN) { - dprintk(2, "read_pipe_completion: err shutdown\n"); + dprintk(2, "%s: err shutdown\n", __func__); pipe_info->err_count++; return; } if (pipe_info->state == 0) { - dprintk(2, "exiting USB pipe"); + dprintk(2, "%s: exiting USB pipe", __func__); return; } @@ -2279,7 +2408,7 @@ static void read_pipe_completion(struct urb *purb) s2255_read_video_callback(dev, pipe_info); else { pipe_info->err_count++; - dprintk(1, "s2255drv: failed URB %d\n", status); + dprintk(1, "%s: failed URB %d\n", __func__, status); } pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); @@ -2295,7 +2424,7 @@ static void read_pipe_completion(struct urb *purb) dev_err(&dev->udev->dev, "error submitting urb\n"); } } else { - dprintk(2, "read pipe complete state 0\n"); + dprintk(2, "%s :complete state 0\n", __func__); } return; } @@ -2304,35 +2433,28 @@ static int s2255_start_readpipe(struct s2255_dev *dev) { int pipe; int retval; - int i; - struct s2255_pipeinfo *pipe_info = dev->pipes; + struct s2255_pipeinfo *pipe_info = &dev->pipe; pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); - dprintk(2, "start pipe IN %d\n", dev->read_endpoint); - - for (i = 0; i < MAX_PIPE_BUFFERS; i++) { - pipe_info->state = 1; - pipe_info->err_count = 0; - pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pipe_info->stream_urb) { - dev_err(&dev->udev->dev, - "ReadStream: Unable to alloc URB\n"); - return -ENOMEM; - } - /* transfer buffer allocated in board_init */ - usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->cur_transfer_size, - read_pipe_completion, pipe_info); - - dprintk(4, "submitting URB %p\n", pipe_info->stream_urb); - retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); - if (retval) { - printk(KERN_ERR "s2255: start read pipe failed\n"); - return retval; - } + dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint); + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&dev->udev->dev, + "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->cur_transfer_size, + read_pipe_completion, pipe_info); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR "s2255: start read pipe failed\n"); + return retval; } - return 0; } @@ -2347,10 +2469,7 @@ static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) dprintk(2, "start acquire failed, bad channel %lu\n", chn); return -1; } - chn_rev = G_chnmap[chn]; - dprintk(1, "S2255: start acquire %lu \n", chn); - buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); @@ -2366,9 +2485,9 @@ static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) } /* send the start command */ - *(u32 *) buffer = IN_DATA_TOKEN; - *((u32 *) buffer + 1) = (u32) chn_rev; - *((u32 *) buffer + 2) = (u32) CMD_START; + *(__le32 *) buffer = IN_DATA_TOKEN; + *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); + *((__le32 *) buffer + 2) = CMD_START; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (res != 0) dev_err(&dev->udev->dev, "CMD_START error\n"); @@ -2383,65 +2502,41 @@ static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) unsigned char *buffer; int res; unsigned long chn_rev; - if (chn >= MAX_CHANNELS) { dprintk(2, "stop acquire failed, bad channel %lu\n", chn); return -1; } chn_rev = G_chnmap[chn]; - buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); return -ENOMEM; } - /* send the stop command */ - dprintk(4, "stop acquire %lu\n", chn); - *(u32 *) buffer = IN_DATA_TOKEN; - *((u32 *) buffer + 1) = (u32) chn_rev; - *((u32 *) buffer + 2) = CMD_STOP; + *(__le32 *) buffer = IN_DATA_TOKEN; + *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); + *((__le32 *) buffer + 2) = CMD_STOP; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - if (res != 0) dev_err(&dev->udev->dev, "CMD_STOP error\n"); - - dprintk(4, "stop acquire: releasing states \n"); - kfree(buffer); dev->b_acquire[chn] = 0; - + dprintk(4, "%s: chn %lu, res %d\n", __func__, chn, res); return res; } static void s2255_stop_readpipe(struct s2255_dev *dev) { - int j; - - if (dev == NULL) { - s2255_dev_err(&dev->udev->dev, "invalid device\n"); - return; - } - dprintk(4, "stop read pipe\n"); - for (j = 0; j < MAX_PIPE_BUFFERS; j++) { - struct s2255_pipeinfo *pipe_info = &dev->pipes[j]; - if (pipe_info) { - if (pipe_info->state == 0) - continue; - pipe_info->state = 0; - } - } + struct s2255_pipeinfo *pipe = &dev->pipe; - for (j = 0; j < MAX_PIPE_BUFFERS; j++) { - struct s2255_pipeinfo *pipe_info = &dev->pipes[j]; - if (pipe_info->stream_urb) { - /* cancel urb */ - usb_kill_urb(pipe_info->stream_urb); - usb_free_urb(pipe_info->stream_urb); - pipe_info->stream_urb = NULL; - } + pipe->state = 0; + if (pipe->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe->stream_urb); + usb_free_urb(pipe->stream_urb); + pipe->stream_urb = NULL; } - dprintk(2, "s2255 stop read pipe: %d\n", j); + dprintk(4, "%s", __func__); return; } @@ -2473,32 +2568,28 @@ static int s2255_probe(struct usb_interface *interface, int retval = -ENOMEM; __le32 *pdata; int fw_size; - - dprintk(2, "s2255: probe\n"); - + dprintk(2, "%s\n", __func__); /* allocate memory for our device state and initialize it to zero */ dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); if (dev == NULL) { s2255_dev_err(&interface->dev, "out of memory\n"); - goto error; + return -ENOMEM; } - + atomic_set(&dev->channels, 0); + dev->pid = id->idProduct; dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); if (!dev->fw_data) - goto error; - + goto errorFWDATA1; mutex_init(&dev->lock); mutex_init(&dev->open_lock); - /* grab usb_device and save it */ dev->udev = usb_get_dev(interface_to_usbdev(interface)); if (dev->udev == NULL) { dev_err(&interface->dev, "null usb device\n"); retval = -ENODEV; - goto error; + goto errorUDEV; } - kref_init(&dev->kref); - dprintk(1, "dev: %p, kref: %p udev %p interface %p\n", dev, &dev->kref, + dprintk(1, "dev: %p, udev %p interface %p\n", dev, dev->udev, interface); dev->interface = interface; /* set up the endpoint information */ @@ -2514,39 +2605,33 @@ static int s2255_probe(struct usb_interface *interface, if (!dev->read_endpoint) { dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); - goto error; + goto errorEP; } - - /* set intfdata */ - usb_set_intfdata(interface, dev); - - dprintk(100, "after intfdata %p\n", dev); - init_timer(&dev->timer); dev->timer.function = s2255_timer; dev->timer.data = (unsigned long)dev->fw_data; - init_waitqueue_head(&dev->fw_data->wait_fw); - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) { init_waitqueue_head(&dev->wait_setmode[i]); - + init_waitqueue_head(&dev->wait_vidstatus[i]); + } dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->fw_data->fw_urb) { dev_err(&interface->dev, "out of memory!\n"); - goto error; + goto errorFWURB; } + dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); if (!dev->fw_data->pfw_data) { dev_err(&interface->dev, "out of memory!\n"); - goto error; + goto errorFWDATA2; } /* load the first chunk */ if (request_firmware(&dev->fw_data->fw, FIRMWARE_FILE_NAME, &dev->udev->dev)) { printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); - goto error; + goto errorREQFW; } /* check the firmware is valid */ fw_size = dev->fw_data->fw->size; @@ -2555,59 +2640,80 @@ static int s2255_probe(struct usb_interface *interface, if (*pdata != S2255_FW_MARKER) { printk(KERN_INFO "Firmware invalid.\n"); retval = -ENODEV; - goto error; + goto errorFWMARKER; } else { /* make sure firmware is the latest */ __le32 *pRel; pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); + dev->dsp_fw_ver = *pRel; + if (*pRel < S2255_CUR_DSP_FWVER) + printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); + if (dev->pid == 0x2257 && *pRel < S2255_MIN_DSP_COLORFILTER) + printk(KERN_WARNING "s2255: 2257 requires firmware %d" + "or above.\n", S2255_MIN_DSP_COLORFILTER); } - /* loads v4l specific */ - s2255_probe_v4l(dev); usb_reset_device(dev->udev); /* load 2255 board specific */ retval = s2255_board_init(dev); if (retval) - goto error; - - dprintk(4, "before probe done %p\n", dev); + goto errorBOARDINIT; spin_lock_init(&dev->slock); - s2255_fwload_start(dev, 0); + /* loads v4l specific */ + retval = s2255_probe_v4l(dev); + if (retval) + goto errorBOARDINIT; dev_info(&interface->dev, "Sensoray 2255 detected\n"); return 0; -error: +errorBOARDINIT: + s2255_board_shutdown(dev); +errorFWMARKER: + release_firmware(dev->fw_data->fw); +errorREQFW: + kfree(dev->fw_data->pfw_data); +errorFWDATA2: + usb_free_urb(dev->fw_data->fw_urb); +errorFWURB: + del_timer(&dev->timer); +errorEP: + usb_put_dev(dev->udev); +errorUDEV: + kfree(dev->fw_data); + mutex_destroy(&dev->open_lock); + mutex_destroy(&dev->lock); +errorFWDATA1: + kfree(dev); + printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval); return retval; } /* disconnect routine. when board is removed physically or with rmmod */ static void s2255_disconnect(struct usb_interface *interface) { - struct s2255_dev *dev = NULL; + struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); int i; - dprintk(1, "s2255: disconnect interface %p\n", interface); - dev = usb_get_intfdata(interface); - - /* - * wake up any of the timers to allow open_lock to be - * acquired sooner - */ + int channels = atomic_read(&dev->channels); + v4l2_device_unregister(&dev->v4l2_dev); + /*see comments in the uvc_driver.c usb disconnect function */ + atomic_inc(&dev->channels); + /* unregister each video device. */ + for (i = 0; i < channels; i++) { + if (video_is_registered(&dev->vdev[i])) + video_unregister_device(&dev->vdev[i]); + } + /* wake up any of our timers */ atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); wake_up(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { dev->setmode_ready[i] = 1; wake_up(&dev->wait_setmode[i]); + dev->vidstatus_ready[i] = 1; + wake_up(&dev->wait_vidstatus[i]); } - - mutex_lock(&dev->open_lock); - usb_set_intfdata(interface, NULL); - mutex_unlock(&dev->open_lock); - - if (dev) { - kref_put(&dev->kref, s2255_destroy); - dprintk(1, "s2255drv: disconnect\n"); - dev_info(&interface->dev, "s2255usb now disconnected\n"); - } + if (atomic_dec_and_test(&dev->channels)) + s2255_destroy(dev); + dev_info(&interface->dev, "%s\n", __func__); } static struct usb_driver s2255_driver = { @@ -2620,15 +2726,12 @@ static struct usb_driver s2255_driver = { static int __init usb_s2255_init(void) { int result; - /* register this driver with the USB subsystem */ result = usb_register(&s2255_driver); - if (result) pr_err(KBUILD_MODNAME - ": usb_register failed. Error number %d\n", result); - - dprintk(2, "s2255_init: done\n"); + ": usb_register failed. Error number %d\n", result); + dprintk(2, "%s\n", __func__); return result; } diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index c0a7f8a369f4..53b6fcde3800 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -74,6 +74,7 @@ struct saa711x_state { int contrast; int hue; int sat; + int chroma_agc; int width; int height; u32 ident; @@ -592,7 +593,7 @@ static const unsigned char saa7115_init_misc[] = { R_5D_DID, 0xbd, R_5E_SDID, 0x35, - R_02_INPUT_CNTL_1, 0x84, /* input tuner -> input 4, amplifier active */ + R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */ R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, @@ -743,6 +744,7 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct saa711x_state *state = to_state(sd); + u8 val; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -784,7 +786,21 @@ static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) state->hue = ctrl->value; saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue); break; - + case V4L2_CID_CHROMA_AGC: + val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL); + state->chroma_agc = ctrl->value; + if (ctrl->value) + val &= 0x7f; + else + val |= 0x80; + saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val); + break; + case V4L2_CID_CHROMA_GAIN: + /* Chroma gain cannot be set when AGC is enabled */ + if (state->chroma_agc == 1) + return -EINVAL; + saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80); + break; default: return -EINVAL; } @@ -809,6 +825,12 @@ static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_HUE: ctrl->value = state->hue; break; + case V4L2_CID_CHROMA_AGC: + ctrl->value = state->chroma_agc; + break; + case V4L2_CID_CHROMA_GAIN: + ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; + break; default: return -EINVAL; } @@ -1069,7 +1091,7 @@ static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_forma saa7115_cfg_vbi_off); } -static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced) { static u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ @@ -1078,11 +1100,8 @@ static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ 0, 0, 0, 0 }; - struct v4l2_sliced_vbi_format *sliced = &fmt->fmt.sliced; int i; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; memset(sliced, 0, sizeof(*sliced)); /* done if using raw VBI */ if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) @@ -1098,16 +1117,27 @@ static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + return saa711x_g_sliced_fmt(sd, &fmt->fmt.sliced); +} + +static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + saa711x_set_lcr(sd, NULL); + return 0; +} + +static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) +{ + saa711x_set_lcr(sd, fmt); + return 0; +} + static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - saa711x_set_lcr(sd, &fmt->fmt.sliced); - return 0; - } - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - saa711x_set_lcr(sd, NULL); - return 0; - } if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1209,6 +1239,10 @@ static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); case V4L2_CID_HUE: return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); + case V4L2_CID_CHROMA_AGC: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + case V4L2_CID_CHROMA_GAIN: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48); default: return -EINVAL; } @@ -1524,18 +1558,25 @@ static const struct v4l2_subdev_video_ops saa711x_video_ops = { .s_crystal_freq = saa711x_s_crystal_freq, .g_fmt = saa711x_g_fmt, .s_fmt = saa711x_s_fmt, - .g_vbi_data = saa711x_g_vbi_data, - .decode_vbi_line = saa711x_decode_vbi_line, .s_stream = saa711x_s_stream, .querystd = saa711x_querystd, .g_input_status = saa711x_g_input_status, }; +static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = { + .g_vbi_data = saa711x_g_vbi_data, + .decode_vbi_line = saa711x_decode_vbi_line, + .g_sliced_fmt = saa711x_g_sliced_fmt, + .s_sliced_fmt = saa711x_s_sliced_fmt, + .s_raw_fmt = saa711x_s_raw_fmt, +}; + static const struct v4l2_subdev_ops saa711x_ops = { .core = &saa711x_core_ops, .tuner = &saa711x_tuner_ops, .audio = &saa711x_audio_ops, .video = &saa711x_video_ops, + .vbi = &saa711x_vbi_ops, }; /* ----------------------------------------------------------------------- */ @@ -1593,6 +1634,7 @@ static int saa711x_probe(struct i2c_client *client, state->contrast = 64; state->hue = 0; state->sat = 64; + state->chroma_agc = 1; switch (chip_id) { case '1': state->ident = V4L2_IDENT_SAA7111; diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 250ef84cf5ca..87986ad62f86 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -625,29 +625,33 @@ static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) return saa7127_set_video_enable(sd, enable); } -static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) { struct saa7127_state *state = to_state(sd); - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - - memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); + memset(fmt, 0, sizeof(*fmt)); if (state->vps_enable) - fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; + fmt->service_lines[0][16] = V4L2_SLICED_VPS; if (state->wss_enable) - fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + fmt->service_lines[0][23] = V4L2_SLICED_WSS_625; if (state->cc_enable) { - fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; - fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; + fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; } - fmt->fmt.sliced.service_set = + fmt->service_set = (state->vps_enable ? V4L2_SLICED_VPS : 0) | (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); return 0; } +static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + return saa7127_g_sliced_fmt(sd, &fmt->fmt.sliced); +} + static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { switch (data->id) { @@ -727,16 +731,21 @@ static const struct v4l2_subdev_core_ops saa7127_core_ops = { }; static const struct v4l2_subdev_video_ops saa7127_video_ops = { - .s_vbi_data = saa7127_s_vbi_data, .g_fmt = saa7127_g_fmt, .s_std_output = saa7127_s_std_output, .s_routing = saa7127_s_routing, .s_stream = saa7127_s_stream, }; +static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = { + .s_vbi_data = saa7127_s_vbi_data, + .g_sliced_fmt = saa7127_g_sliced_fmt, +}; + static const struct v4l2_subdev_ops saa7127_ops = { .core = &saa7127_core_ops, .video = &saa7127_video_ops, + .vbi = &saa7127_vbi_ops, }; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index d48c450ed77c..d3bd82ad010a 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -1011,8 +1011,6 @@ static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) unsigned int idx; int err, addr; - if (snd_BUG_ON(!chip)) - return -EINVAL; strcpy(card->mixername, "SAA7134 Mixer"); for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) { diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 297833fb3b4a..72700d4e3941 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5355,6 +5355,79 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE2, }, }, + [SAA7134_BOARD_HAWELL_HW_404M7] = { + /* Hawell HW-404M7 & Hawell HW-808M7 */ + /* Bogoslovskiy Viktor <bogovic@bk.ru> */ + .name = "Hawell HW-404M7", + .audio_clock = 0x00200000, + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x389c00, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x01fc00, + } }, + }, + [SAA7134_BOARD_BEHOLD_H7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV H7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_BEHOLD_A7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV A7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, }; @@ -6549,6 +6622,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = SAA7134_BOARD_UNKNOWN, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7190, + .driver_data = SAA7134_BOARD_BEHOLD_H7, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7090, + .driver_data = SAA7134_BOARD_BEHOLD_A7, },{ /* --- end of list --- */ } @@ -6602,6 +6687,8 @@ static int saa7134_xc5000_callback(struct saa7134_dev *dev, { switch (dev->board) { case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: if (command == XC5000_TUNER_RESET) { /* Down and UP pheripherial RESET pin for reset all chips */ saa_writeb(SAA7134_SPECIAL_MODE, 0x00); @@ -6973,6 +7060,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: @@ -7215,6 +7304,11 @@ int saa7134_board_init2(struct saa7134_dev *dev) printk(KERN_INFO "%s: P7131 analog only, using " "entry of %s\n", dev->name, saa7134_boards[dev->board].name); + + /* IR init has already happened for other cards, so + * we have to catch up. */ + dev->has_remote = SAA7134_REMOTE_GPIO; + saa7134_input_init1(dev); } break; case SAA7134_BOARD_HAUPPAUGE_HVR1150: @@ -7344,6 +7438,23 @@ int saa7134_board_init2(struct saa7134_dev *dev) } break; } + case SAA7134_BOARD_BEHOLD_H6: + { + u8 data[] = { 0x09, 0x9f, 0x86, 0x11}; + struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = data, + .len = sizeof(data)}; + + /* The tuner TUNER_PHILIPS_FMD1216MEX_MK3 after hardware */ + /* start has disabled IF and enabled DVB-T. When saa7134 */ + /* scan I2C devices it not detect IF tda9887 and can`t */ + /* watch TV without software reboot. For solve this problem */ + /* switch the tuner to analog TV mode manually. */ + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + printk(KERN_WARNING + "%s: Unable to enable IF of the tuner.\n", + dev->name); + break; + } } /* switch() */ /* initialize tuner */ diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index a7ad7810fddc..90f231881297 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -471,7 +471,7 @@ static char *irqbits[] = { "DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3", "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC", "TRIG_ERR", "CONF_ERR", "LOAD_ERR", - "GPIO16?", "GPIO18", "GPIO22", "GPIO23" + "GPIO16", "GPIO18", "GPIO22", "GPIO23" }; #define IRQBITS ARRAY_SIZE(irqbits) @@ -601,12 +601,14 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id) /* disable gpio16 IRQ */ printk(KERN_WARNING "%s/irq: looping -- " "clearing GPIO16 enable bit\n",dev->name); - saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_P); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_N); } else if (report & SAA7134_IRQ_REPORT_GPIO18) { /* disable gpio18 IRQs */ printk(KERN_WARNING "%s/irq: looping -- " "clearing GPIO18 enable bit\n",dev->name); - saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_N); } else { /* disable all irqs */ printk(KERN_WARNING "%s/irq: looping -- " @@ -698,11 +700,13 @@ static int saa7134_hw_enable2(struct saa7134_dev *dev) if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) { if (dev->remote->mask_keydown & 0x10000) - irq2_mask |= SAA7134_IRQ2_INTE_GPIO16; - else if (dev->remote->mask_keydown & 0x40000) - irq2_mask |= SAA7134_IRQ2_INTE_GPIO18; - else if (dev->remote->mask_keyup & 0x40000) - irq2_mask |= SAA7134_IRQ2_INTE_GPIO18A; + irq2_mask |= SAA7134_IRQ2_INTE_GPIO16_N; + else { /* Allow enabling both IRQ edge triggers */ + if (dev->remote->mask_keydown & 0x40000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_P; + if (dev->remote->mask_keyup & 0x40000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_N; + } } if (dev->has_remote == SAA7134_REMOTE_I2C) { @@ -1227,7 +1231,7 @@ static int saa7134_resume(struct pci_dev *pci_dev) if (card_has_mpeg(dev)) saa7134_ts_init_hw(dev); if (dev->remote) - saa7134_ir_start(dev, dev->remote); + saa7134_ir_start(dev); saa7134_hw_enable1(dev); msleep(100); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 4ab4a987c9b9..31e82be1b7e7 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1532,6 +1532,15 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, &behold_x7_tunerconfig); } break; + case SAA7134_BOARD_BEHOLD_H7: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &behold_x7_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(xc5000_attach, fe0->dvb.frontend, + &dev->i2c_adap, &behold_x7_tunerconfig); + } + break; case SAA7134_BOARD_AVERMEDIA_A700_PRO: case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: /* Zarlink ZL10313 */ diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 58a0cdc8414a..e5565e2fd426 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -28,6 +28,8 @@ #include "saa7134-reg.h" #include "saa7134.h" +#define MODULE_NAME "saa7134" + static unsigned int disable_ir; module_param(disable_ir, int, 0444); MODULE_PARM_DESC(disable_ir,"disable infrared remote support"); @@ -66,6 +68,7 @@ MODULE_PARM_DESC(disable_other_ir, "disable full codes of " /* Helper functions for RC5 and NEC decoding at GPIO16 or GPIO18 */ static int saa7134_rc5_irq(struct saa7134_dev *dev); static int saa7134_nec_irq(struct saa7134_dev *dev); +static int saa7134_raw_decode_irq(struct saa7134_dev *dev); static void nec_task(unsigned long data); static void saa7134_nec_timer(unsigned long data); @@ -397,14 +400,23 @@ static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) void saa7134_input_irq(struct saa7134_dev *dev) { - struct card_ir *ir = dev->remote; + struct card_ir *ir; + + if (!dev || !dev->remote) + return; + + ir = dev->remote; + if (!ir->running) + return; if (ir->nec_gpio) { saa7134_nec_irq(dev); - } else if (!ir->polling && !ir->rc5_gpio) { + } else if (!ir->polling && !ir->rc5_gpio && !ir->raw_decode) { build_key(dev); } else if (ir->rc5_gpio) { saa7134_rc5_irq(dev); + } else if (ir->raw_decode) { + saa7134_raw_decode_irq(dev); } } @@ -417,8 +429,32 @@ static void saa7134_input_timer(unsigned long data) mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); } -void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir) +void ir_raw_decode_timer_end(unsigned long data) +{ + struct saa7134_dev *dev = (struct saa7134_dev *)data; + struct card_ir *ir = dev->remote; + + ir_raw_event_handle(dev->remote->dev); + + ir->active = 0; +} + +static int __saa7134_ir_start(void *priv) { + struct saa7134_dev *dev = priv; + struct card_ir *ir; + + if (!dev) + return -EINVAL; + + ir = dev->remote; + if (!ir) + return -EINVAL; + + if (ir->running) + return 0; + + ir->running = 1; if (ir->polling) { setup_timer(&ir->timer, saa7134_input_timer, (unsigned long)dev); @@ -441,26 +477,125 @@ void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir) setup_timer(&ir->timer_keyup, saa7134_nec_timer, (unsigned long)dev); tasklet_init(&ir->tlet, nec_task, (unsigned long)dev); + } else if (ir->raw_decode) { + /* set timer_end for code completion */ + init_timer(&ir->timer_end); + ir->timer_end.function = ir_raw_decode_timer_end; + ir->timer_end.data = (unsigned long)dev; + ir->active = 0; } + + return 0; } -void saa7134_ir_stop(struct saa7134_dev *dev) +static void __saa7134_ir_stop(void *priv) { + struct saa7134_dev *dev = priv; + struct card_ir *ir; + + if (!dev) + return; + + ir = dev->remote; + if (!ir) + return; + + if (!ir->running) + return; if (dev->remote->polling) del_timer_sync(&dev->remote->timer); + else if (ir->rc5_gpio) + del_timer_sync(&ir->timer_end); + else if (ir->nec_gpio) + tasklet_kill(&ir->tlet); + else if (ir->raw_decode) { + del_timer_sync(&ir->timer_end); + ir->active = 0; + } + + ir->running = 0; + + return; +} + +int saa7134_ir_start(struct saa7134_dev *dev) +{ + if (dev->remote->users) + return __saa7134_ir_start(dev); + + return 0; +} + +void saa7134_ir_stop(struct saa7134_dev *dev) +{ + if (dev->remote->users) + __saa7134_ir_stop(dev); +} + +static int saa7134_ir_open(void *priv) +{ + struct saa7134_dev *dev = priv; + + dev->remote->users++; + return __saa7134_ir_start(dev); +} + +static void saa7134_ir_close(void *priv) +{ + struct saa7134_dev *dev = priv; + + dev->remote->users--; + if (!dev->remote->users) + __saa7134_ir_stop(dev); +} + + +int saa7134_ir_change_protocol(void *priv, u64 ir_type) +{ + struct saa7134_dev *dev = priv; + struct card_ir *ir = dev->remote; + u32 nec_gpio, rc5_gpio; + + if (ir_type == IR_TYPE_RC5) { + dprintk("Changing protocol to RC5\n"); + nec_gpio = 0; + rc5_gpio = 1; + } else if (ir_type == IR_TYPE_NEC) { + dprintk("Changing protocol to NEC\n"); + nec_gpio = 1; + rc5_gpio = 0; + } else { + dprintk("IR protocol type %ud is not supported\n", + (unsigned)ir_type); + return -EINVAL; + } + + if (ir->running) { + saa7134_ir_stop(dev); + ir->nec_gpio = nec_gpio; + ir->rc5_gpio = rc5_gpio; + saa7134_ir_start(dev); + } else { + ir->nec_gpio = nec_gpio; + ir->rc5_gpio = rc5_gpio; + } + + return 0; } int saa7134_input_init1(struct saa7134_dev *dev) { struct card_ir *ir; struct input_dev *input_dev; - struct ir_scancode_table *ir_codes = NULL; + char *ir_codes = NULL; u32 mask_keycode = 0; u32 mask_keydown = 0; u32 mask_keyup = 0; int polling = 0; int rc5_gpio = 0; int nec_gpio = 0; + int raw_decode = 0; + int allow_protocol_change = 0; u64 ir_type = IR_TYPE_OTHER; int err; @@ -476,27 +611,27 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYTVPLATINUM_FM: case SAA7134_BOARD_FLYTVPLATINUM_MINI2: case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: - ir_codes = &ir_codes_flyvideo_table; + ir_codes = RC_MAP_FLYVIDEO; mask_keycode = 0xEC00000; mask_keydown = 0x0040000; break; case SAA7134_BOARD_CINERGY400: case SAA7134_BOARD_CINERGY600: case SAA7134_BOARD_CINERGY600_MK3: - ir_codes = &ir_codes_cinergy_table; + ir_codes = RC_MAP_CINERGY; mask_keycode = 0x00003f; mask_keyup = 0x040000; break; case SAA7134_BOARD_ECS_TVP3XP: case SAA7134_BOARD_ECS_TVP3XP_4CB5: - ir_codes = &ir_codes_eztv_table; + ir_codes = RC_MAP_EZTV; mask_keycode = 0x00017c; mask_keyup = 0x000002; polling = 50; // ms break; case SAA7134_BOARD_KWORLD_XPERT: case SAA7134_BOARD_AVACSSMARTTV: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; mask_keycode = 0x00001F; mask_keyup = 0x000020; polling = 50; // ms @@ -513,7 +648,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_GO_007_FM: case SAA7134_BOARD_AVERMEDIA_M102: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: - ir_codes = &ir_codes_avermedia_table; + ir_codes = RC_MAP_AVERMEDIA; mask_keycode = 0x0007C8; mask_keydown = 0x000010; polling = 50; // ms @@ -522,14 +657,15 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); break; case SAA7134_BOARD_AVERMEDIA_M135A: - ir_codes = &ir_codes_avermedia_m135a_table; - mask_keydown = 0x0040000; - mask_keycode = 0x00013f; - nec_gpio = 1; + ir_codes = RC_MAP_AVERMEDIA_M135A_RM_JX; + mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = 1; break; case SAA7134_BOARD_AVERMEDIA_777: case SAA7134_BOARD_AVERMEDIA_A16AR: - ir_codes = &ir_codes_avermedia_table; + ir_codes = RC_MAP_AVERMEDIA; mask_keycode = 0x02F200; mask_keydown = 0x000400; polling = 50; // ms @@ -538,7 +674,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; case SAA7134_BOARD_AVERMEDIA_A16D: - ir_codes = &ir_codes_avermedia_a16d_table; + ir_codes = RC_MAP_AVERMEDIA_A16D; mask_keycode = 0x02F200; mask_keydown = 0x000400; polling = 50; /* ms */ @@ -547,14 +683,14 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; case SAA7134_BOARD_KWORLD_TERMINATOR: - ir_codes = &ir_codes_pixelview_table; + ir_codes = RC_MAP_PIXELVIEW; mask_keycode = 0x00001f; mask_keyup = 0x000060; polling = 50; // ms break; case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: - ir_codes = &ir_codes_manli_table; + ir_codes = RC_MAP_MANLI; mask_keycode = 0x001f00; mask_keyup = 0x004000; polling = 50; /* ms */ @@ -574,25 +710,25 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_BEHOLD_507RDS_MK3: case SAA7134_BOARD_BEHOLD_507RDS_MK5: - ir_codes = &ir_codes_manli_table; + ir_codes = RC_MAP_MANLI; mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; /* ms */ break; case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: - ir_codes = &ir_codes_behold_columbus_table; + ir_codes = RC_MAP_BEHOLD_COLUMBUS; mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; // ms break; case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: - ir_codes = &ir_codes_pctv_sedna_table; + ir_codes = RC_MAP_PCTV_SEDNA; mask_keycode = 0x001f00; mask_keyup = 0x004000; polling = 50; // ms break; case SAA7134_BOARD_GOTVIEW_7135: - ir_codes = &ir_codes_gotview7135_table; + ir_codes = RC_MAP_GOTVIEW7135; mask_keycode = 0x0003CC; mask_keydown = 0x000010; polling = 5; /* ms */ @@ -601,80 +737,80 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_VIDEOMATE_TV_PVR: case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: - ir_codes = &ir_codes_videomate_tv_pvr_table; + ir_codes = RC_MAP_VIDEOMATE_TV_PVR; mask_keycode = 0x00003F; mask_keyup = 0x400000; polling = 50; // ms break; case SAA7134_BOARD_PROTEUS_2309: - ir_codes = &ir_codes_proteus_2309_table; + ir_codes = RC_MAP_PROTEUS_2309; mask_keycode = 0x00007F; mask_keyup = 0x000080; polling = 50; // ms break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: - ir_codes = &ir_codes_videomate_tv_pvr_table; + ir_codes = RC_MAP_VIDEOMATE_TV_PVR; mask_keycode = 0x003F00; mask_keyup = 0x040000; break; case SAA7134_BOARD_FLYDVBS_LR300: case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_FLYDVBTDUO: - ir_codes = &ir_codes_flydvb_table; + ir_codes = RC_MAP_FLYDVB; mask_keycode = 0x0001F00; mask_keydown = 0x0040000; break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: - ir_codes = &ir_codes_asus_pc39_table; + ir_codes = RC_MAP_ASUS_PC39; mask_keydown = 0x0040000; rc5_gpio = 1; break; case SAA7134_BOARD_ENCORE_ENLTV: case SAA7134_BOARD_ENCORE_ENLTV_FM: - ir_codes = &ir_codes_encore_enltv_table; + ir_codes = RC_MAP_ENCORE_ENLTV; mask_keycode = 0x00007f; mask_keyup = 0x040000; polling = 50; // ms break; case SAA7134_BOARD_ENCORE_ENLTV_FM53: - ir_codes = &ir_codes_encore_enltv_fm53_table; + ir_codes = RC_MAP_ENCORE_ENLTV_FM53; mask_keydown = 0x0040000; mask_keycode = 0x00007f; nec_gpio = 1; break; case SAA7134_BOARD_10MOONSTVMASTER3: - ir_codes = &ir_codes_encore_enltv_table; + ir_codes = RC_MAP_ENCORE_ENLTV; mask_keycode = 0x5f80000; mask_keyup = 0x8000000; polling = 50; //ms break; case SAA7134_BOARD_GENIUS_TVGO_A11MCE: - ir_codes = &ir_codes_genius_tvgo_a11mce_table; + ir_codes = RC_MAP_GENIUS_TVGO_A11MCE; mask_keycode = 0xff; mask_keydown = 0xf00000; polling = 50; /* ms */ break; case SAA7134_BOARD_REAL_ANGEL_220: - ir_codes = &ir_codes_real_audio_220_32_keys_table; + ir_codes = RC_MAP_REAL_AUDIO_220_32_KEYS; mask_keycode = 0x3f00; mask_keyup = 0x4000; polling = 50; /* ms */ break; case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: - ir_codes = &ir_codes_kworld_plus_tv_analog_table; + ir_codes = RC_MAP_KWORLD_PLUS_TV_ANALOG; mask_keycode = 0x7f; polling = 40; /* ms */ break; case SAA7134_BOARD_VIDEOMATE_S350: - ir_codes = &ir_codes_videomate_s350_table; + ir_codes = RC_MAP_VIDEOMATE_S350; mask_keycode = 0x003f00; mask_keydown = 0x040000; break; case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: - ir_codes = &ir_codes_winfast_table; + ir_codes = RC_MAP_WINFAST; mask_keycode = 0x5f00; mask_keyup = 0x020000; polling = 50; /* ms */ @@ -695,6 +831,9 @@ int saa7134_input_init1(struct saa7134_dev *dev) } ir->dev = input_dev; + dev->remote = ir; + + ir->running = 0; /* init hardware-specific stuff */ ir->mask_keycode = mask_keycode; @@ -703,6 +842,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) ir->polling = polling; ir->rc5_gpio = rc5_gpio; ir->nec_gpio = nec_gpio; + ir->raw_decode = raw_decode; /* init input device */ snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)", @@ -710,6 +850,19 @@ int saa7134_input_init1(struct saa7134_dev *dev) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); + + ir->props.priv = dev; + ir->props.open = saa7134_ir_open; + ir->props.close = saa7134_ir_close; + + if (raw_decode) + ir->props.driver_type = RC_DRIVER_IR_RAW; + + if (!raw_decode && allow_protocol_change) { + ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; + ir->props.change_protocol = saa7134_ir_change_protocol; + } + err = ir_input_init(input_dev, &ir->ir, ir_type); if (err < 0) goto err_out_free; @@ -727,12 +880,9 @@ int saa7134_input_init1(struct saa7134_dev *dev) } input_dev->dev.parent = &dev->pci->dev; - dev->remote = ir; - saa7134_ir_start(dev, ir); - - err = ir_input_register(ir->dev, ir_codes, NULL); + err = ir_input_register(ir->dev, ir_codes, &ir->props, MODULE_NAME); if (err) - goto err_out_stop; + goto err_out_free; /* the remote isn't as bouncy as a keyboard */ ir->dev->rep[REP_DELAY] = repeat_delay; @@ -740,10 +890,8 @@ int saa7134_input_init1(struct saa7134_dev *dev) return 0; - err_out_stop: - saa7134_ir_stop(dev); +err_out_free: dev->remote = NULL; - err_out_free: kfree(ir); return err; } @@ -787,24 +935,24 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "Pinnacle PCTV"; if (pinnacle_remote == 0) { dev->init_data.get_key = get_key_pinnacle_color; - dev->init_data.ir_codes = &ir_codes_pinnacle_color_table; + dev->init_data.ir_codes = RC_MAP_PINNACLE_COLOR; info.addr = 0x47; } else { dev->init_data.get_key = get_key_pinnacle_grey; - dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; info.addr = 0x47; } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: dev->init_data.name = "Purple TV"; dev->init_data.get_key = get_key_purpletv; - dev->init_data.ir_codes = &ir_codes_purpletv_table; + dev->init_data.ir_codes = RC_MAP_PURPLETV; info.addr = 0x7a; break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: dev->init_data.name = "MSI TV@nywhere Plus"; dev->init_data.get_key = get_key_msi_tvanywhere_plus; - dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table; + dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS; info.addr = 0x30; /* MSI TV@nywhere Plus controller doesn't seem to respond to probes unless we read something from @@ -818,7 +966,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_HAUPPAUGE_HVR1110: dev->init_data.name = "HVR 1110"; dev->init_data.get_key = get_key_hvr1110; - dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE_NEW; info.addr = 0x71; break; case SAA7134_BOARD_BEHOLD_607FM_MK3: @@ -834,9 +982,12 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: dev->init_data.name = "BeholdTV"; dev->init_data.get_key = get_key_beholdm6xx; - dev->init_data.ir_codes = &ir_codes_behold_table; + dev->init_data.ir_codes = RC_MAP_BEHOLD; + dev->init_data.type = IR_TYPE_NEC; info.addr = 0x2d; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: @@ -846,7 +997,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVB_TRIO: dev->init_data.name = "FlyDVB Trio"; dev->init_data.get_key = get_key_flydvb_trio; - dev->init_data.ir_codes = &ir_codes_flydvb_table; + dev->init_data.ir_codes = RC_MAP_FLYDVB; info.addr = 0x0b; break; default: @@ -859,6 +1010,33 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) i2c_new_device(&dev->i2c_adap, &info); } +static int saa7134_raw_decode_irq(struct saa7134_dev *dev) +{ + struct card_ir *ir = dev->remote; + unsigned long timeout; + int space; + + /* Generate initial event */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown; + ir_raw_event_store_edge(dev->remote->dev, space ? IR_SPACE : IR_PULSE); + + + /* + * Wait 15 ms from the start of the first IR event before processing + * the event. This time is enough for NEC protocol. May need adjustments + * to work with other protocols. + */ + if (!ir->active) { + timeout = jiffies + jiffies_to_msecs(15); + mod_timer(&ir->timer_end, timeout); + ir->active = 1; + } + + return 1; +} + static int saa7134_rc5_irq(struct saa7134_dev *dev) { struct card_ir *ir = dev->remote; @@ -901,7 +1079,6 @@ static int saa7134_rc5_irq(struct saa7134_dev *dev) return 1; } - /* On NEC protocol, One has 2.25 ms, and zero has 1.125 ms The first pulse (start) has 9 + 4.5 ms */ @@ -1011,14 +1188,14 @@ static void nec_task(unsigned long data) /* Keep repeating the last key */ mod_timer(&ir->timer_keyup, jiffies + msecs_to_jiffies(150)); - saa_setl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); + saa_setl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); } static int saa7134_nec_irq(struct saa7134_dev *dev) { struct card_ir *ir = dev->remote; - saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); tasklet_schedule(&ir->tlet); return 1; diff --git a/drivers/media/video/saa7134/saa7134-reg.h b/drivers/media/video/saa7134/saa7134-reg.h index cf89d96d7295..e7e0af101fa7 100644 --- a/drivers/media/video/saa7134/saa7134-reg.h +++ b/drivers/media/video/saa7134/saa7134-reg.h @@ -112,17 +112,17 @@ #define SAA7134_IRQ1_INTE_RA0_0 (1 << 0) #define SAA7134_IRQ2 (0x2c8 >> 2) -#define SAA7134_IRQ2_INTE_GPIO23A (1 << 17) -#define SAA7134_IRQ2_INTE_GPIO23 (1 << 16) -#define SAA7134_IRQ2_INTE_GPIO22A (1 << 15) -#define SAA7134_IRQ2_INTE_GPIO22 (1 << 14) -#define SAA7134_IRQ2_INTE_GPIO18A (1 << 13) -#define SAA7134_IRQ2_INTE_GPIO18 (1 << 12) -#define SAA7134_IRQ2_INTE_GPIO16 (1 << 11) /* not certain */ -#define SAA7134_IRQ2_INTE_SC2 (1 << 10) -#define SAA7134_IRQ2_INTE_SC1 (1 << 9) -#define SAA7134_IRQ2_INTE_SC0 (1 << 8) -#define SAA7134_IRQ2_INTE_DEC5 (1 << 7) +#define SAA7134_IRQ2_INTE_GPIO23_N (1 << 17) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO23_P (1 << 16) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO22_N (1 << 15) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO22_P (1 << 14) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO18_N (1 << 13) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO18_P (1 << 12) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO16_N (1 << 11) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO16_P (1 << 10) /* positive edge */ +#define SAA7134_IRQ2_INTE_SC2 (1 << 9) +#define SAA7134_IRQ2_INTE_SC1 (1 << 8) +#define SAA7134_IRQ2_INTE_SC0 (1 << 7) #define SAA7134_IRQ2_INTE_DEC4 (1 << 6) #define SAA7134_IRQ2_INTE_DEC3 (1 << 5) #define SAA7134_IRQ2_INTE_DEC2 (1 << 4) @@ -135,7 +135,7 @@ #define SAA7134_IRQ_REPORT_GPIO23 (1 << 17) #define SAA7134_IRQ_REPORT_GPIO22 (1 << 16) #define SAA7134_IRQ_REPORT_GPIO18 (1 << 15) -#define SAA7134_IRQ_REPORT_GPIO16 (1 << 14) /* not certain */ +#define SAA7134_IRQ_REPORT_GPIO16 (1 << 14) #define SAA7134_IRQ_REPORT_LOAD_ERR (1 << 13) #define SAA7134_IRQ_REPORT_CONF_ERR (1 << 12) #define SAA7134_IRQ_REPORT_TRIG_ERR (1 << 11) diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 31138d3e51bb..45f0ac8f3c0f 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1180,7 +1180,7 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str That needs to be fixed somehow, but for now this is good enough. */ if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -1359,7 +1359,7 @@ static int video_open(struct file *file) fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); fh->width = 720; fh->height = 576; - v4l2_prio_open(&dev->prio,&fh->prio); + v4l2_prio_open(&dev->prio, &fh->prio); videobuf_queue_sg_init(&fh->cap, &video_qops, &dev->pci->dev, &dev->slock, @@ -1502,7 +1502,7 @@ static int video_release(struct file *file) saa7134_pgtable_free(dev->pci,&fh->pt_cap); saa7134_pgtable_free(dev->pci,&fh->pt_vbi); - v4l2_prio_close(&dev->prio,&fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); return 0; @@ -1632,8 +1632,15 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = field; - v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, - &f->fmt.pix.height, 32, maxh, 0, 0); + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.width &= ~0x03; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = @@ -1778,7 +1785,7 @@ static int saa7134_s_input(struct file *file, void *priv, unsigned int i) struct saa7134_dev *dev = fh->dev; int err; - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; @@ -1832,7 +1839,7 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_ That needs to be fixed somehow, but for now this is good enough. */ if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } else if (res_locked(dev, RESOURCE_OVERLAY)) { @@ -2016,7 +2023,7 @@ static int saa7134_s_tuner(struct file *file, void *priv, struct saa7134_dev *dev = fh->dev; int rx, mode, err; - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; @@ -2050,7 +2057,7 @@ static int saa7134_s_frequency(struct file *file, void *priv, struct saa7134_dev *dev = fh->dev; int err; - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index bf130967ed17..3962534267be 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -20,7 +20,7 @@ */ #include <linux/version.h> -#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,15) +#define SAA7134_VERSION_CODE KERNEL_VERSION(0, 2, 16) #include <linux/pci.h> #include <linux/i2c.h> @@ -300,6 +300,9 @@ struct saa7134_format { #define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174 #define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175 #define SAA7134_BOARD_BEHOLD_505RDS_MK3 176 +#define SAA7134_BOARD_HAWELL_HW_404M7 177 +#define SAA7134_BOARD_BEHOLD_H7 178 +#define SAA7134_BOARD_BEHOLD_A7 179 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -809,7 +812,7 @@ int saa7134_input_init1(struct saa7134_dev *dev); void saa7134_input_fini(struct saa7134_dev *dev); void saa7134_input_irq(struct saa7134_dev *dev); void saa7134_probe_i2c_ir(struct saa7134_dev *dev); -void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir); +int saa7134_ir_start(struct saa7134_dev *dev); void saa7134_ir_stop(struct saa7134_dev *dev); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 1ad980f8e770..4ac3b482fbb4 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -115,9 +115,20 @@ struct sh_mobile_ceu_dev { }; struct sh_mobile_ceu_cam { - struct v4l2_rect ceu_rect; - unsigned int cam_width; - unsigned int cam_height; + /* CEU offsets within scaled by the CEU camera output */ + unsigned int ceu_left; + unsigned int ceu_top; + /* Client output, as seen by the CEU */ + unsigned int width; + unsigned int height; + /* + * User window from S_CROP / G_CROP, produced by client cropping and + * scaling, CEU scaling and CEU cropping, mapped back onto the client + * input window + */ + struct v4l2_rect subrect; + /* Camera cropping rectangle */ + struct v4l2_rect rect; const struct soc_mbus_pixelfmt *extra_fmt; enum v4l2_mbus_pixelcode code; }; @@ -213,8 +224,8 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, *count = 2; if (pcdev->video_limit) { - while (PAGE_ALIGN(*size) * *count > pcdev->video_limit) - (*count)--; + if (PAGE_ALIGN(*size) * *count > pcdev->video_limit) + *count = pcdev->video_limit / PAGE_ALIGN(*size); } dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); @@ -565,38 +576,36 @@ static u16 calc_scale(unsigned int src, unsigned int *dst) } /* rect is guaranteed to not exceed the scaled camera rectangle */ -static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd, - unsigned int out_width, - unsigned int out_height) +static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *rect = &cam->ceu_rect; struct sh_mobile_ceu_dev *pcdev = ici->priv; unsigned int height, width, cdwdr_width, in_width, in_height; unsigned int left_offset, top_offset; u32 camor; - dev_dbg(icd->dev.parent, "Crop %ux%u@%u:%u\n", - rect->width, rect->height, rect->left, rect->top); + dev_geo(icd->dev.parent, "Crop %ux%u@%u:%u\n", + icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top); - left_offset = rect->left; - top_offset = rect->top; + left_offset = cam->ceu_left; + top_offset = cam->ceu_top; + /* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */ if (pcdev->image_mode) { - in_width = rect->width; + in_width = cam->width; if (!pcdev->is_16bit) { in_width *= 2; left_offset *= 2; } - width = out_width; - cdwdr_width = out_width; + width = icd->user_width; + cdwdr_width = icd->user_width; } else { - int bytes_per_line = soc_mbus_bytes_per_line(out_width, + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); unsigned int w_factor; - width = out_width; + width = icd->user_width; switch (icd->current_fmt->host_fmt->packing) { case SOC_MBUS_PACKING_2X8_PADHI: @@ -606,17 +615,17 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd, w_factor = 1; } - in_width = rect->width * w_factor; + in_width = cam->width * w_factor; left_offset = left_offset * w_factor; if (bytes_per_line < 0) - cdwdr_width = out_width; + cdwdr_width = icd->user_width; else cdwdr_width = bytes_per_line; } - height = out_height; - in_height = rect->height; + height = icd->user_height; + in_height = cam->height; if (V4L2_FIELD_NONE != pcdev->field) { height /= 2; in_height /= 2; @@ -775,9 +784,10 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, } ceu_write(pcdev, CAIFR, value); - sh_mobile_ceu_set_rect(icd, icd->user_width, icd->user_height); + sh_mobile_ceu_set_rect(icd); mdelay(1); + dev_geo(icd->dev.parent, "CFLCR 0x%x\n", pcdev->cflcr); ceu_write(pcdev, CFLCR, pcdev->cflcr); /* @@ -866,6 +876,8 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) fmt->packing == SOC_MBUS_PACKING_EXTEND16); } +static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); + static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, struct soc_camera_format_xlate *xlate) { @@ -894,10 +906,55 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, return 0; if (!icd->host_priv) { + struct v4l2_mbus_framefmt mf; + struct v4l2_rect rect; + struct device *dev = icd->dev.parent; + int shift = 0; + + /* FIXME: subwindow is lost between close / open */ + + /* Cache current client geometry */ + ret = client_g_rect(sd, &rect); + if (ret < 0) + return ret; + + /* First time */ + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; + + while ((mf.width > 2560 || mf.height > 1920) && shift < 4) { + /* Try 2560x1920, 1280x960, 640x480, 320x240 */ + mf.width = 2560 >> shift; + mf.height = 1920 >> shift; + ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + if (ret < 0) + return ret; + shift++; + } + + if (shift == 4) { + dev_err(dev, "Failed to configure the client below %ux%x\n", + mf.width, mf.height); + return -EIO; + } + + dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height); + cam = kzalloc(sizeof(*cam), GFP_KERNEL); if (!cam) return -ENOMEM; + /* We are called with current camera crop, initialise subrect with it */ + cam->rect = rect; + cam->subrect = rect; + + cam->width = mf.width; + cam->height = mf.height; + + cam->width = mf.width; + cam->height = mf.height; + icd->host_priv = cam; } else { cam = icd->host_priv; @@ -979,16 +1036,12 @@ static unsigned int scale_down(unsigned int size, unsigned int scale) return (size * 4096 + scale / 2) / scale; } -static unsigned int scale_up(unsigned int size, unsigned int scale) -{ - return (size * scale + 2048) / 4096; -} - static unsigned int calc_generic_scale(unsigned int input, unsigned int output) { return (input * 4096 + output / 2) / output; } +/* Get and store current client crop */ static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) { struct v4l2_crop crop; @@ -1007,25 +1060,51 @@ static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = v4l2_subdev_call(sd, video, cropcap, &cap); - if (ret < 0) - return ret; - - *rect = cap.defrect; + if (!ret) + *rect = cap.defrect; return ret; } +/* Client crop has changed, update our sub-rectangle to remain within the area */ +static void update_subrect(struct sh_mobile_ceu_cam *cam) +{ + struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect; + + if (rect->width < subrect->width) + subrect->width = rect->width; + + if (rect->height < subrect->height) + subrect->height = rect->height; + + if (rect->left > subrect->left) + subrect->left = rect->left; + else if (rect->left + rect->width > + subrect->left + subrect->width) + subrect->left = rect->left + rect->width - + subrect->width; + + if (rect->top > subrect->top) + subrect->top = rect->top; + else if (rect->top + rect->height > + subrect->top + subrect->height) + subrect->top = rect->top + rect->height - + subrect->height; +} + /* * The common for both scaling and cropping iterative approach is: * 1. try if the client can produce exactly what requested by the user * 2. if (1) failed, try to double the client image until we get one big enough * 3. if (2) failed, try to request the maximum image */ -static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, +static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, struct v4l2_crop *cam_crop) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; struct device *dev = sd->v4l2_dev->dev; + struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_cropcap cap; int ret; unsigned int width, height; @@ -1041,13 +1120,14 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, */ if (!memcmp(rect, cam_rect, sizeof(*rect))) { /* Even if camera S_CROP failed, but camera rectangle matches */ - dev_dbg(dev, "Camera S_CROP successful for %ux%u@%u:%u\n", + dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n", rect->width, rect->height, rect->left, rect->top); + cam->rect = *cam_rect; return 0; } /* Try to fix cropping, that camera hasn't managed to set */ - dev_geo(dev, "Fix camera S_CROP for %ux%u@%u:%u to %ux%u@%u:%u\n", + dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n", cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top, rect->width, rect->height, rect->left, rect->top); @@ -1057,6 +1137,7 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, if (ret < 0) return ret; + /* Put user requested rectangle within sensor bounds */ soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, cap.bounds.width); soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, @@ -1069,6 +1150,10 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, width = max(cam_rect->width, 2); height = max(cam_rect->height, 2); + /* + * Loop as long as sensor is not covering the requested rectangle and + * is still within its bounds + */ while (!ret && (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) && (cap.bounds.width > width || cap.bounds.height > height)) { @@ -1086,6 +1171,7 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, * target left, set it to the middle point between the current * left and minimum left. But that would add too much * complexity: we would have to iterate each border separately. + * Instead we just drop to the left and top bounds. */ if (cam_rect->left > rect->left) cam_rect->left = cap.bounds.left; @@ -1103,7 +1189,7 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, v4l2_subdev_call(sd, video, s_crop, cam_crop); ret = client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for %ux%u@%u:%u\n", ret, + dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); } @@ -1117,82 +1203,24 @@ static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, *cam_rect = cap.bounds; v4l2_subdev_call(sd, video, s_crop, cam_crop); ret = client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for max %ux%u@%u:%u\n", ret, + dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); } - return ret; -} - -static int get_camera_scales(struct v4l2_subdev *sd, struct v4l2_rect *rect, - unsigned int *scale_h, unsigned int *scale_v) -{ - struct v4l2_mbus_framefmt mf; - int ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); - if (ret < 0) - return ret; - - *scale_h = calc_generic_scale(rect->width, mf.width); - *scale_v = calc_generic_scale(rect->height, mf.height); - - return 0; -} - -static int get_camera_subwin(struct soc_camera_device *icd, - struct v4l2_rect *cam_subrect, - unsigned int cam_hscale, unsigned int cam_vscale) -{ - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *ceu_rect = &cam->ceu_rect; - - if (!ceu_rect->width) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->dev.parent; - struct v4l2_mbus_framefmt mf; - int ret; - /* First time */ - - ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); - if (ret < 0) - return ret; - - dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height); - - if (mf.width > 2560) { - ceu_rect->width = 2560; - ceu_rect->left = (mf.width - 2560) / 2; - } else { - ceu_rect->width = mf.width; - ceu_rect->left = 0; - } - - if (mf.height > 1920) { - ceu_rect->height = 1920; - ceu_rect->top = (mf.height - 1920) / 2; - } else { - ceu_rect->height = mf.height; - ceu_rect->top = 0; - } - - dev_geo(dev, "initialised CEU rect %ux%u@%u:%u\n", - ceu_rect->width, ceu_rect->height, - ceu_rect->left, ceu_rect->top); + if (!ret) { + cam->rect = *cam_rect; + update_subrect(cam); } - cam_subrect->width = scale_up(ceu_rect->width, cam_hscale); - cam_subrect->left = scale_up(ceu_rect->left, cam_hscale); - cam_subrect->height = scale_up(ceu_rect->height, cam_vscale); - cam_subrect->top = scale_up(ceu_rect->top, cam_vscale); - - return 0; + return ret; } +/* Iterative s_mbus_fmt, also updates cached client crop on success */ static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt *mf, bool ceu_can_scale) { + struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->dev.parent; unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; @@ -1200,6 +1228,15 @@ static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_cropcap cap; int ret; + ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); + if (ret < 0) + return ret; + + dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); + + if ((width == mf->width && height == mf->height) || !ceu_can_scale) + goto update_cache; + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = v4l2_subdev_call(sd, video, cropcap, &cap); @@ -1209,15 +1246,6 @@ static int client_s_fmt(struct soc_camera_device *icd, max_width = min(cap.bounds.width, 2560); max_height = min(cap.bounds.height, 1920); - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); - if (ret < 0) - return ret; - - dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); - - if ((width == mf->width && height == mf->height) || !ceu_can_scale) - return 0; - /* Camera set a format, but geometry is not precise, try to improve */ tmp_w = mf->width; tmp_h = mf->height; @@ -1239,26 +1267,37 @@ static int client_s_fmt(struct soc_camera_device *icd, } } +update_cache: + /* Update cache */ + ret = client_g_rect(sd, &cam->rect); + if (ret < 0) + return ret; + + update_subrect(cam); + return 0; } /** - * @rect - camera cropped rectangle - * @sub_rect - CEU cropped rectangle, mapped back to camera input area - * @ceu_rect - on output calculated CEU crop rectangle + * @width - on output: user width, mapped back to input + * @height - on output: user height, mapped back to input + * @mf - in- / output camera output window */ -static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect, - struct v4l2_rect *sub_rect, struct v4l2_rect *ceu_rect, - struct v4l2_mbus_framefmt *mf, bool ceu_can_scale) +static int client_scale(struct soc_camera_device *icd, + struct v4l2_mbus_framefmt *mf, + unsigned int *width, unsigned int *height, + bool ceu_can_scale) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct sh_mobile_ceu_cam *cam = icd->host_priv; struct device *dev = icd->dev.parent; struct v4l2_mbus_framefmt mf_tmp = *mf; unsigned int scale_h, scale_v; int ret; - /* 5. Apply iterative camera S_FMT for camera user window. */ + /* + * 5. Apply iterative camera S_FMT for camera user window (also updates + * client crop cache and the imaginary sub-rectangle). + */ ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale); if (ret < 0) return ret; @@ -1270,60 +1309,22 @@ static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect, /* unneeded - it is already in "mf_tmp" */ - /* 7. Calculate new camera scales. */ - ret = get_camera_scales(sd, rect, &scale_h, &scale_v); - if (ret < 0) - return ret; - - dev_geo(dev, "7: camera scales %u:%u\n", scale_h, scale_v); + /* 7. Calculate new client scales. */ + scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width); + scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height); - cam->cam_width = mf_tmp.width; - cam->cam_height = mf_tmp.height; mf->width = mf_tmp.width; mf->height = mf_tmp.height; mf->colorspace = mf_tmp.colorspace; /* * 8. Calculate new CEU crop - apply camera scales to previously - * calculated "effective" crop. + * updated "effective" crop. */ - ceu_rect->left = scale_down(sub_rect->left, scale_h); - ceu_rect->width = scale_down(sub_rect->width, scale_h); - ceu_rect->top = scale_down(sub_rect->top, scale_v); - ceu_rect->height = scale_down(sub_rect->height, scale_v); + *width = scale_down(cam->subrect.width, scale_h); + *height = scale_down(cam->subrect.height, scale_v); - dev_geo(dev, "8: new CEU rect %ux%u@%u:%u\n", - ceu_rect->width, ceu_rect->height, - ceu_rect->left, ceu_rect->top); - - return 0; -} - -/* Get combined scales */ -static int get_scales(struct soc_camera_device *icd, - unsigned int *scale_h, unsigned int *scale_v) -{ - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_crop cam_crop; - unsigned int width_in, height_in; - int ret; - - cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = client_g_rect(sd, &cam_crop.c); - if (ret < 0) - return ret; - - ret = get_camera_scales(sd, &cam_crop.c, scale_h, scale_v); - if (ret < 0) - return ret; - - width_in = scale_up(cam->ceu_rect.width, *scale_h); - height_in = scale_up(cam->ceu_rect.height, *scale_v); - - *scale_h = calc_generic_scale(width_in, icd->user_width); - *scale_v = calc_generic_scale(height_in, icd->user_height); + dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); return 0; } @@ -1342,115 +1343,165 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, struct sh_mobile_ceu_dev *pcdev = ici->priv; struct v4l2_crop cam_crop; struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *cam_rect = &cam_crop.c, *ceu_rect = &cam->ceu_rect; + struct v4l2_rect *cam_rect = &cam_crop.c; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->dev.parent; struct v4l2_mbus_framefmt mf; - unsigned int scale_comb_h, scale_comb_v, scale_ceu_h, scale_ceu_v, - out_width, out_height; + unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v, + out_width, out_height, scale_h, scale_v; + int interm_width, interm_height; u32 capsr, cflcr; int ret; - /* 1. Calculate current combined scales. */ - ret = get_scales(icd, &scale_comb_h, &scale_comb_v); - if (ret < 0) - return ret; + dev_geo(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, + rect->left, rect->top); - dev_geo(dev, "1: combined scales %u:%u\n", scale_comb_h, scale_comb_v); + /* During camera cropping its output window can change too, stop CEU */ + capsr = capture_save_reset(pcdev); + dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); - /* 2. Apply iterative camera S_CROP for new input window. */ - ret = client_s_crop(sd, a, &cam_crop); + /* 1. - 2. Apply iterative camera S_CROP for new input window. */ + ret = client_s_crop(icd, a, &cam_crop); if (ret < 0) return ret; - dev_geo(dev, "2: camera cropped to %ux%u@%u:%u\n", + dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n", cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); /* On success cam_crop contains current camera crop */ - /* - * 3. If old combined scales applied to new crop produce an impossible - * user window, adjust scales to produce nearest possible window. - */ - out_width = scale_down(rect->width, scale_comb_h); - out_height = scale_down(rect->height, scale_comb_v); - - if (out_width > 2560) - out_width = 2560; - else if (out_width < 2) - out_width = 2; - - if (out_height > 1920) - out_height = 1920; - else if (out_height < 4) - out_height = 4; - - dev_geo(dev, "3: Adjusted output %ux%u\n", out_width, out_height); - - /* 4. Use G_CROP to retrieve actual input window: already in cam_crop */ - - /* - * 5. Using actual input window and calculated combined scales calculate - * camera target output window. - */ - mf.width = scale_down(cam_rect->width, scale_comb_h); - mf.height = scale_down(cam_rect->height, scale_comb_v); - - dev_geo(dev, "5: camera target %ux%u\n", mf.width, mf.height); - - /* 6. - 9. */ - mf.code = cam->code; - mf.field = pcdev->field; - - capsr = capture_save_reset(pcdev); - dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); + /* 3. Retrieve camera output window */ + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; - /* Make relative to camera rectangle */ - rect->left -= cam_rect->left; - rect->top -= cam_rect->top; + if (mf.width > 2560 || mf.height > 1920) + return -EINVAL; - ret = client_scale(icd, cam_rect, rect, ceu_rect, &mf, - pcdev->image_mode && - V4L2_FIELD_NONE == pcdev->field); + /* Cache camera output window */ + cam->width = mf.width; + cam->height = mf.height; - dev_geo(dev, "6-9: %d\n", ret); + /* 4. Calculate camera scales */ + scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); + scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); - /* 10. Use CEU cropping to crop to the new window. */ - sh_mobile_ceu_set_rect(icd, out_width, out_height); + /* Calculate intermediate window */ + interm_width = scale_down(rect->width, scale_cam_h); + interm_height = scale_down(rect->height, scale_cam_v); - dev_geo(dev, "10: CEU cropped to %ux%u@%u:%u\n", - ceu_rect->width, ceu_rect->height, - ceu_rect->left, ceu_rect->top); + if (pcdev->image_mode) { + out_width = min(interm_width, icd->user_width); + out_height = min(interm_height, icd->user_height); + } else { + out_width = interm_width; + out_height = interm_height; + } /* - * 11. Calculate CEU scales from camera scales from results of (10) and - * user window from (3) + * 5. Calculate CEU scales from camera scales from results of (5) and + * the user window */ - scale_ceu_h = calc_scale(ceu_rect->width, &out_width); - scale_ceu_v = calc_scale(ceu_rect->height, &out_height); + scale_ceu_h = calc_scale(interm_width, &out_width); + scale_ceu_v = calc_scale(interm_height, &out_height); - dev_geo(dev, "11: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); + /* Calculate camera scales */ + scale_h = calc_generic_scale(cam_rect->width, out_width); + scale_v = calc_generic_scale(cam_rect->height, out_height); - /* 12. Apply CEU scales. */ + dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); + + /* Apply CEU scales. */ cflcr = scale_ceu_h | (scale_ceu_v << 16); if (cflcr != pcdev->cflcr) { pcdev->cflcr = cflcr; ceu_write(pcdev, CFLCR, cflcr); } + icd->user_width = out_width; + icd->user_height = out_height; + cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_h) & ~1; + cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_v) & ~1; + + /* 6. Use CEU cropping to crop to the new window. */ + sh_mobile_ceu_set_rect(icd); + + cam->subrect = *rect; + + dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n", + icd->user_width, icd->user_height, + cam->ceu_left, cam->ceu_top); + /* Restore capture */ if (pcdev->active) capsr |= 1; capture_restore(pcdev, capsr); - icd->user_width = out_width; - icd->user_height = out_height; - /* Even if only camera cropping succeeded */ return ret; } +static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd, + struct v4l2_crop *a) +{ + struct sh_mobile_ceu_cam *cam = icd->host_priv; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->c = cam->subrect; + + return 0; +} + +/* + * Calculate real client output window by applying new scales to the current + * client crop. New scales are calculated from the requested output format and + * CEU crop, mapped backed onto the client input (subrect). + */ +static void calculate_client_output(struct soc_camera_device *icd, + struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf) +{ + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct device *dev = icd->dev.parent; + struct v4l2_rect *cam_subrect = &cam->subrect; + unsigned int scale_v, scale_h; + + if (cam_subrect->width == cam->rect.width && + cam_subrect->height == cam->rect.height) { + /* No sub-cropping */ + mf->width = pix->width; + mf->height = pix->height; + return; + } + + /* 1.-2. Current camera scales and subwin - cached. */ + + dev_geo(dev, "2: subwin %ux%u@%u:%u\n", + cam_subrect->width, cam_subrect->height, + cam_subrect->left, cam_subrect->top); + + /* + * 3. Calculate new combined scales from input sub-window to requested + * user window. + */ + + /* + * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF + * (128x96) or larger than VGA + */ + scale_h = calc_generic_scale(cam_subrect->width, pix->width); + scale_v = calc_generic_scale(cam_subrect->height, pix->height); + + dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); + + /* + * 4. Calculate client output window by applying combined scales to real + * input window. + */ + mf->width = scale_down(cam->rect.width, scale_h); + mf->height = scale_down(cam->rect.height, scale_v); +} + /* Similar to set_crop multistage iterative algorithm */ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) @@ -1460,18 +1511,17 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_mbus_framefmt mf; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->dev.parent; __u32 pixfmt = pix->pixelformat; const struct soc_camera_format_xlate *xlate; - struct v4l2_crop cam_crop; - struct v4l2_rect *cam_rect = &cam_crop.c, cam_subrect, ceu_rect; - unsigned int scale_cam_h, scale_cam_v; + unsigned int ceu_sub_width, ceu_sub_height; u16 scale_v, scale_h; int ret; bool image_mode; enum v4l2_field field; + dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height); + switch (pix->field) { default: pix->field = V4L2_FIELD_NONE; @@ -1492,46 +1542,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* 1. Calculate current camera scales. */ - cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = client_g_rect(sd, cam_rect); - if (ret < 0) - return ret; - - ret = get_camera_scales(sd, cam_rect, &scale_cam_h, &scale_cam_v); - if (ret < 0) - return ret; - - dev_geo(dev, "1: camera scales %u:%u\n", scale_cam_h, scale_cam_v); - - /* - * 2. Calculate "effective" input crop (sensor subwindow) - CEU crop - * scaled back at current camera scales onto input window. - */ - ret = get_camera_subwin(icd, &cam_subrect, scale_cam_h, scale_cam_v); - if (ret < 0) - return ret; - - dev_geo(dev, "2: subwin %ux%u@%u:%u\n", - cam_subrect.width, cam_subrect.height, - cam_subrect.left, cam_subrect.top); - - /* - * 3. Calculate new combined scales from "effective" input window to - * requested user window. - */ - scale_h = calc_generic_scale(cam_subrect.width, pix->width); - scale_v = calc_generic_scale(cam_subrect.height, pix->height); - - dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); - - /* - * 4. Calculate camera output window by applying combined scales to real - * input window. - */ - mf.width = scale_down(cam_rect->width, scale_h); - mf.height = scale_down(cam_rect->height, scale_v); + /* 1.-4. Calculate client output geometry */ + calculate_client_output(icd, &f->fmt.pix, &mf); mf.field = pix->field; mf.colorspace = pix->colorspace; mf.code = xlate->code; @@ -1547,17 +1559,17 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, image_mode = false; } - dev_geo(dev, "4: camera output %ux%u\n", mf.width, mf.height); + dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height); /* 5. - 9. */ - ret = client_scale(icd, cam_rect, &cam_subrect, &ceu_rect, &mf, + ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height, image_mode && V4L2_FIELD_NONE == field); - dev_geo(dev, "5-9: client scale %d\n", ret); + dev_geo(dev, "5-9: client scale return %d\n", ret); /* Done with the camera. Now see if we can improve the result */ - dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n", + dev_geo(dev, "Camera %d fmt %ux%u, requested %ux%u\n", ret, mf.width, mf.height, pix->width, pix->height); if (ret < 0) return ret; @@ -1565,40 +1577,44 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, if (mf.code != xlate->code) return -EINVAL; + /* 9. Prepare CEU crop */ + cam->width = mf.width; + cam->height = mf.height; + /* 10. Use CEU scaling to scale to the requested user window. */ /* We cannot scale up */ - if (pix->width > mf.width) - pix->width = mf.width; - if (pix->width > ceu_rect.width) - pix->width = ceu_rect.width; + if (pix->width > ceu_sub_width) + ceu_sub_width = pix->width; - if (pix->height > mf.height) - pix->height = mf.height; - if (pix->height > ceu_rect.height) - pix->height = ceu_rect.height; + if (pix->height > ceu_sub_height) + ceu_sub_height = pix->height; pix->colorspace = mf.colorspace; if (image_mode) { /* Scale pix->{width x height} down to width x height */ - scale_h = calc_scale(ceu_rect.width, &pix->width); - scale_v = calc_scale(ceu_rect.height, &pix->height); - - pcdev->cflcr = scale_h | (scale_v << 16); + scale_h = calc_scale(ceu_sub_width, &pix->width); + scale_v = calc_scale(ceu_sub_height, &pix->height); } else { - pix->width = ceu_rect.width; - pix->height = ceu_rect.height; - scale_h = scale_v = 0; - pcdev->cflcr = 0; + pix->width = ceu_sub_width; + pix->height = ceu_sub_height; + scale_h = 0; + scale_v = 0; } + pcdev->cflcr = scale_h | (scale_v << 16); + + /* + * We have calculated CFLCR, the actual configuration will be performed + * in sh_mobile_ceu_set_bus_param() + */ + dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n", - ceu_rect.width, scale_h, pix->width, - ceu_rect.height, scale_v, pix->height); + ceu_sub_width, scale_h, pix->width, + ceu_sub_height, scale_v, pix->height); cam->code = xlate->code; - cam->ceu_rect = ceu_rect; icd->current_fmt = xlate; pcdev->field = field; @@ -1820,6 +1836,7 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .remove = sh_mobile_ceu_remove_device, .get_formats = sh_mobile_ceu_get_formats, .put_formats = sh_mobile_ceu_put_formats, + .get_crop = sh_mobile_ceu_get_crop, .set_crop = sh_mobile_ceu_set_crop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c new file mode 100644 index 000000000000..f5b892a2a8ee --- /dev/null +++ b/drivers/media/video/sh_vou.c @@ -0,0 +1,1476 @@ +/* + * SuperH Video Output Unit (VOU) driver + * + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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 <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/version.h> +#include <linux/videodev2.h> + +#include <media/sh_vou.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mediabus.h> +#include <media/videobuf-dma-contig.h> + +/* Mirror addresses are not available for all registers */ +#define VOUER 0 +#define VOUCR 4 +#define VOUSTR 8 +#define VOUVCR 0xc +#define VOUISR 0x10 +#define VOUBCR 0x14 +#define VOUDPR 0x18 +#define VOUDSR 0x1c +#define VOUVPR 0x20 +#define VOUIR 0x24 +#define VOUSRR 0x28 +#define VOUMSR 0x2c +#define VOUHIR 0x30 +#define VOUDFR 0x34 +#define VOUAD1R 0x38 +#define VOUAD2R 0x3c +#define VOUAIR 0x40 +#define VOUSWR 0x44 +#define VOURCR 0x48 +#define VOURPR 0x50 + +enum sh_vou_status { + SH_VOU_IDLE, + SH_VOU_INITIALISING, + SH_VOU_RUNNING, +}; + +#define VOU_MAX_IMAGE_WIDTH 720 +#define VOU_MAX_IMAGE_HEIGHT 480 + +struct sh_vou_device { + struct v4l2_device v4l2_dev; + struct video_device *vdev; + atomic_t use_count; + struct sh_vou_pdata *pdata; + spinlock_t lock; + void __iomem *base; + /* State information */ + struct v4l2_pix_format pix; + struct v4l2_rect rect; + struct list_head queue; + v4l2_std_id std; + int pix_idx; + struct videobuf_buffer *active; + enum sh_vou_status status; +}; + +struct sh_vou_file { + struct videobuf_queue vbq; +}; + +/* Register access routines for sides A, B and mirror addresses */ +static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value) +{ + __raw_writel(value, vou_dev->base + reg); +} + +static void sh_vou_reg_ab_write(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value) +{ + __raw_writel(value, vou_dev->base + reg); + __raw_writel(value, vou_dev->base + reg + 0x1000); +} + +static void sh_vou_reg_m_write(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value) +{ + __raw_writel(value, vou_dev->base + reg + 0x2000); +} + +static u32 sh_vou_reg_a_read(struct sh_vou_device *vou_dev, unsigned int reg) +{ + return __raw_readl(vou_dev->base + reg); +} + +static void sh_vou_reg_a_set(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value, u32 mask) +{ + u32 old = __raw_readl(vou_dev->base + reg); + + value = (value & mask) | (old & ~mask); + __raw_writel(value, vou_dev->base + reg); +} + +static void sh_vou_reg_b_set(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value, u32 mask) +{ + sh_vou_reg_a_set(vou_dev, reg + 0x1000, value, mask); +} + +static void sh_vou_reg_ab_set(struct sh_vou_device *vou_dev, unsigned int reg, + u32 value, u32 mask) +{ + sh_vou_reg_a_set(vou_dev, reg, value, mask); + sh_vou_reg_b_set(vou_dev, reg, value, mask); +} + +struct sh_vou_fmt { + u32 pfmt; + char *desc; + unsigned char bpp; + unsigned char rgb; + unsigned char yf; + unsigned char pkf; +}; + +/* Further pixel formats can be added */ +static struct sh_vou_fmt vou_fmt[] = { + { + .pfmt = V4L2_PIX_FMT_NV12, + .bpp = 12, + .desc = "YVU420 planar", + .yf = 0, + .rgb = 0, + }, + { + .pfmt = V4L2_PIX_FMT_NV16, + .bpp = 16, + .desc = "YVYU planar", + .yf = 1, + .rgb = 0, + }, + { + .pfmt = V4L2_PIX_FMT_RGB24, + .bpp = 24, + .desc = "RGB24", + .pkf = 2, + .rgb = 1, + }, + { + .pfmt = V4L2_PIX_FMT_RGB565, + .bpp = 16, + .desc = "RGB565", + .pkf = 3, + .rgb = 1, + }, + { + .pfmt = V4L2_PIX_FMT_RGB565X, + .bpp = 16, + .desc = "RGB565 byteswapped", + .pkf = 3, + .rgb = 1, + }, +}; + +static void sh_vou_schedule_next(struct sh_vou_device *vou_dev, + struct videobuf_buffer *vb) +{ + dma_addr_t addr1, addr2; + + addr1 = videobuf_to_dma_contig(vb); + switch (vou_dev->pix.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + addr2 = addr1 + vou_dev->pix.width * vou_dev->pix.height; + break; + default: + addr2 = 0; + } + + sh_vou_reg_m_write(vou_dev, VOUAD1R, addr1); + sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2); +} + +static void sh_vou_stream_start(struct sh_vou_device *vou_dev, + struct videobuf_buffer *vb) +{ + unsigned int row_coeff; +#ifdef __LITTLE_ENDIAN + u32 dataswap = 7; +#else + u32 dataswap = 0; +#endif + + switch (vou_dev->pix.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + row_coeff = 1; + break; + case V4L2_PIX_FMT_RGB565: + dataswap ^= 1; + case V4L2_PIX_FMT_RGB565X: + row_coeff = 2; + break; + case V4L2_PIX_FMT_RGB24: + row_coeff = 3; + break; + } + + sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap); + sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff); + sh_vou_schedule_next(vou_dev, vb); +} + +static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + BUG_ON(in_interrupt()); + + /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */ + videobuf_waiton(vb, 0, 0); + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +/* Locking: caller holds vq->vb_lock mutex */ +static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct video_device *vdev = vq->priv_data; + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width * + vou_dev->pix.height / 8; + + if (*count < 2) + *count = 2; + + /* Taking into account maximum frame size, *count will stay >= 2 */ + if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024) + *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size); + + dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size); + + return 0; +} + +/* Locking: caller holds vq->vb_lock mutex */ +static int sh_vou_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct video_device *vdev = vq->priv_data; + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct v4l2_pix_format *pix = &vou_dev->pix; + int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; + int ret; + + dev_dbg(vq->dev, "%s()\n", __func__); + + if (vb->width != pix->width || + vb->height != pix->height || + vb->field != pix->field) { + vb->width = pix->width; + vb->height = pix->height; + vb->field = field; + if (vb->state != VIDEOBUF_NEEDS_INIT) + free_buffer(vq, vb); + } + + vb->size = vb->height * bytes_per_line; + if (vb->baddr && vb->bsize < vb->size) { + /* User buffer too small */ + dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n", + vb->bsize, vb->baddr); + return -EINVAL; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret < 0) { + dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n", + vb->memory, ret); + return ret; + } + vb->state = VIDEOBUF_PREPARED; + } + + dev_dbg(vq->dev, + "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n", + __func__, vou_dev->pix_idx, bytes_per_line, + videobuf_to_dma_contig(vb), vb->memory, vb->state); + + return 0; +} + +/* Locking: caller holds vq->vb_lock mutex and vq->irqlock spinlock */ +static void sh_vou_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct video_device *vdev = vq->priv_data; + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + dev_dbg(vq->dev, "%s()\n", __func__); + + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &vou_dev->queue); + + if (vou_dev->status == SH_VOU_RUNNING) { + return; + } else if (!vou_dev->active) { + vou_dev->active = vb; + /* Start from side A: we use mirror addresses, so, set B */ + sh_vou_reg_a_write(vou_dev, VOURPR, 1); + dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__, + sh_vou_reg_a_read(vou_dev, VOUSTR)); + sh_vou_schedule_next(vou_dev, vb); + /* Only activate VOU after the second buffer */ + } else if (vou_dev->active->queue.next == &vb->queue) { + /* Second buffer - initialise register side B */ + sh_vou_reg_a_write(vou_dev, VOURPR, 0); + sh_vou_stream_start(vou_dev, vb); + + /* Register side switching with frame VSYNC */ + sh_vou_reg_a_write(vou_dev, VOURCR, 5); + dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__, + sh_vou_reg_a_read(vou_dev, VOUSTR)); + + /* Enable End-of-Frame (VSYNC) interrupts */ + sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); + /* Two buffers on the queue - activate the hardware */ + + vou_dev->status = SH_VOU_RUNNING; + sh_vou_reg_a_write(vou_dev, VOUER, 0x107); + } +} + +static void sh_vou_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct video_device *vdev = vq->priv_data; + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + unsigned long flags; + + dev_dbg(vq->dev, "%s()\n", __func__); + + spin_lock_irqsave(&vou_dev->lock, flags); + + if (vou_dev->active == vb) { + /* disable output */ + sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); + /* ...but the current frame will complete */ + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); + vou_dev->active = NULL; + } + + if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) { + vb->state = VIDEOBUF_ERROR; + list_del(&vb->queue); + } + + spin_unlock_irqrestore(&vou_dev->lock, flags); + + free_buffer(vq, vb); +} + +static struct videobuf_queue_ops sh_vou_video_qops = { + .buf_setup = sh_vou_buf_setup, + .buf_prepare = sh_vou_buf_prepare, + .buf_queue = sh_vou_buf_queue, + .buf_release = sh_vou_buf_release, +}; + +/* Video IOCTLs */ +static int sh_vou_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + strlcpy(cap->card, "SuperH VOU", sizeof(cap->card)); + cap->version = KERNEL_VERSION(0, 1, 0); + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + return 0; +} + +/* Enumerate formats, that the device can accept from the user */ +static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct sh_vou_file *vou_file = priv; + + if (fmt->index >= ARRAY_SIZE(vou_fmt)) + return -EINVAL; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + strlcpy(fmt->description, vou_fmt[fmt->index].desc, + sizeof(fmt->description)); + fmt->pixelformat = vou_fmt[fmt->index].pfmt; + + return 0; +} + +static int sh_vou_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + fmt->fmt.pix = vou_dev->pix; + + return 0; +} + +static const unsigned char vou_scale_h_num[] = {1, 9, 2, 9, 4}; +static const unsigned char vou_scale_h_den[] = {1, 8, 1, 4, 1}; +static const unsigned char vou_scale_h_fld[] = {0, 2, 1, 3}; +static const unsigned char vou_scale_v_num[] = {1, 2, 4}; +static const unsigned char vou_scale_v_den[] = {1, 1, 1}; +static const unsigned char vou_scale_v_fld[] = {0, 1}; + +static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev, + int pix_idx, int w_idx, int h_idx) +{ + struct sh_vou_fmt *fmt = vou_fmt + pix_idx; + unsigned int black_left, black_top, width_max, height_max, + frame_in_height, frame_out_height, frame_out_top; + struct v4l2_rect *rect = &vou_dev->rect; + struct v4l2_pix_format *pix = &vou_dev->pix; + u32 vouvcr = 0, dsr_h, dsr_v; + + if (vou_dev->std & V4L2_STD_525_60) { + width_max = 858; + height_max = 262; + } else { + width_max = 864; + height_max = 312; + } + + frame_in_height = pix->height / 2; + frame_out_height = rect->height / 2; + frame_out_top = rect->top / 2; + + /* + * Cropping scheme: max useful image is 720x480, and the total video + * area is 858x525 (NTSC) or 864x625 (PAL). AK8813 / 8814 starts + * sampling data beginning with fixed 276th (NTSC) / 288th (PAL) clock, + * of which the first 33 / 25 clocks HSYNC must be held active. This + * has to be configured in CR[HW]. 1 pixel equals 2 clock periods. + * This gives CR[HW] = 16 / 12, VPR[HVP] = 138 / 144, which gives + * exactly 858 - 138 = 864 - 144 = 720! We call the out-of-display area, + * beyond DSR, specified on the left and top by the VPR register "black + * pixels" and out-of-image area (DPR) "background pixels." We fix VPR + * at 138 / 144 : 20, because that's the HSYNC timing, that our first + * client requires, and that's exactly what leaves us 720 pixels for the + * image; we leave VPR[VVP] at default 20 for now, because the client + * doesn't seem to have any special requirements for it. Otherwise we + * could also set it to max - 240 = 22 / 72. Thus VPR depends only on + * the selected standard, and DPR and DSR are selected according to + * cropping. Q: how does the client detect the first valid line? Does + * HSYNC stay inactive during invalid (black) lines? + */ + black_left = width_max - VOU_MAX_IMAGE_WIDTH; + black_top = 20; + + dsr_h = rect->width + rect->left; + dsr_v = frame_out_height + frame_out_top; + + dev_dbg(vou_dev->v4l2_dev.dev, + "image %ux%u, black %u:%u, offset %u:%u, display %ux%u\n", + pix->width, frame_in_height, black_left, black_top, + rect->left, frame_out_top, dsr_h, dsr_v); + + /* VOUISR height - half of a frame height in frame mode */ + sh_vou_reg_ab_write(vou_dev, VOUISR, (pix->width << 16) | frame_in_height); + sh_vou_reg_ab_write(vou_dev, VOUVPR, (black_left << 16) | black_top); + sh_vou_reg_ab_write(vou_dev, VOUDPR, (rect->left << 16) | frame_out_top); + sh_vou_reg_ab_write(vou_dev, VOUDSR, (dsr_h << 16) | dsr_v); + + /* + * if necessary, we could set VOUHIR to + * max(black_left + dsr_h, width_max) here + */ + + if (w_idx) + vouvcr |= (1 << 15) | (vou_scale_h_fld[w_idx - 1] << 4); + if (h_idx) + vouvcr |= (1 << 14) | vou_scale_v_fld[h_idx - 1]; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s: scaling 0x%x\n", fmt->desc, vouvcr); + + /* To produce a colour bar for testing set bit 23 of VOUVCR */ + sh_vou_reg_ab_write(vou_dev, VOUVCR, vouvcr); + sh_vou_reg_ab_write(vou_dev, VOUDFR, + fmt->pkf | (fmt->yf << 8) | (fmt->rgb << 16)); +} + +struct sh_vou_geometry { + struct v4l2_rect output; + unsigned int in_width; + unsigned int in_height; + int scale_idx_h; + int scale_idx_v; +}; + +/* + * Find input geometry, that we can use to produce output, closest to the + * requested rectangle, using VOU scaling + */ +static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) +{ + /* The compiler cannot know, that best and idx will indeed be set */ + unsigned int best_err = UINT_MAX, best = 0, width_max, height_max; + int i, idx = 0; + + if (std & V4L2_STD_525_60) { + width_max = 858; + height_max = 262; + } else { + width_max = 864; + height_max = 312; + } + + /* Image width must be a multiple of 4 */ + v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2, + &geo->in_height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + + /* Select scales to come as close as possible to the output image */ + for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) { + unsigned int err; + unsigned int found = geo->output.width * vou_scale_h_den[i] / + vou_scale_h_num[i]; + + if (found > VOU_MAX_IMAGE_WIDTH) + /* scales increase */ + break; + + err = abs(found - geo->in_width); + if (err < best_err) { + best_err = err; + idx = i; + best = found; + } + if (!err) + break; + } + + geo->in_width = best; + geo->scale_idx_h = idx; + + best_err = UINT_MAX; + + /* This loop can be replaced with one division */ + for (i = ARRAY_SIZE(vou_scale_v_num) - 1; i >= 0; i--) { + unsigned int err; + unsigned int found = geo->output.height * vou_scale_v_den[i] / + vou_scale_v_num[i]; + + if (found > VOU_MAX_IMAGE_HEIGHT) + /* scales increase */ + break; + + err = abs(found - geo->in_height); + if (err < best_err) { + best_err = err; + idx = i; + best = found; + } + if (!err) + break; + } + + geo->in_height = best; + geo->scale_idx_v = idx; +} + +/* + * Find output geometry, that we can produce, using VOU scaling, closest to + * the requested rectangle + */ +static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) +{ + unsigned int best_err = UINT_MAX, best, width_max, height_max; + int i, idx; + + if (std & V4L2_STD_525_60) { + width_max = 858; + height_max = 262 * 2; + } else { + width_max = 864; + height_max = 312 * 2; + } + + /* Select scales to come as close as possible to the output image */ + for (i = 0; i < ARRAY_SIZE(vou_scale_h_num); i++) { + unsigned int err; + unsigned int found = geo->in_width * vou_scale_h_num[i] / + vou_scale_h_den[i]; + + if (found > VOU_MAX_IMAGE_WIDTH) + /* scales increase */ + break; + + err = abs(found - geo->output.width); + if (err < best_err) { + best_err = err; + idx = i; + best = found; + } + if (!err) + break; + } + + geo->output.width = best; + geo->scale_idx_h = idx; + if (geo->output.left + best > width_max) + geo->output.left = width_max - best; + + pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width, + vou_scale_h_num[idx], vou_scale_h_den[idx], best); + + best_err = UINT_MAX; + + /* This loop can be replaced with one division */ + for (i = 0; i < ARRAY_SIZE(vou_scale_v_num); i++) { + unsigned int err; + unsigned int found = geo->in_height * vou_scale_v_num[i] / + vou_scale_v_den[i]; + + if (found > VOU_MAX_IMAGE_HEIGHT) + /* scales increase */ + break; + + err = abs(found - geo->output.height); + if (err < best_err) { + best_err = err; + idx = i; + best = found; + } + if (!err) + break; + } + + geo->output.height = best; + geo->scale_idx_v = idx; + if (geo->output.top + best > height_max) + geo->output.top = height_max - best; + + pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height, + vou_scale_v_num[idx], vou_scale_v_den[idx], best); +} + +static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int pix_idx; + struct sh_vou_geometry geo; + struct v4l2_mbus_framefmt mbfmt = { + /* Revisit: is this the correct code? */ + .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .field = V4L2_FIELD_INTERLACED, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }; + int ret; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, + vou_dev->rect.width, vou_dev->rect.height, + pix->width, pix->height); + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + pix->field != V4L2_FIELD_NONE) + return -EINVAL; + + for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) + if (vou_fmt[pix_idx].pfmt == pix->pixelformat) + break; + + if (pix_idx == ARRAY_SIZE(vou_fmt)) + return -EINVAL; + + /* Image width must be a multiple of 4 */ + v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2, + &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + + geo.in_width = pix->width; + geo.in_height = pix->height; + geo.output = vou_dev->rect; + + vou_adjust_output(&geo, vou_dev->std); + + mbfmt.width = geo.output.width; + mbfmt.height = geo.output.height; + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, + s_mbus_fmt, &mbfmt); + /* Must be implemented, so, don't check for -ENOIOCTLCMD */ + if (ret < 0) + return ret; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, + geo.output.width, geo.output.height, mbfmt.width, mbfmt.height); + + /* Sanity checks */ + if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH || + (unsigned)mbfmt.height > VOU_MAX_IMAGE_HEIGHT || + mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + return -EIO; + + if (mbfmt.width != geo.output.width || + mbfmt.height != geo.output.height) { + geo.output.width = mbfmt.width; + geo.output.height = mbfmt.height; + + vou_adjust_input(&geo, vou_dev->std); + } + + /* We tried to preserve output rectangle, but it could have changed */ + vou_dev->rect = geo.output; + pix->width = geo.in_width; + pix->height = geo.in_height; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u\n", __func__, + pix->width, pix->height); + + vou_dev->pix_idx = pix_idx; + + vou_dev->pix = *pix; + + sh_vou_configure_geometry(vou_dev, pix_idx, + geo.scale_idx_h, geo.scale_idx_v); + + return 0; +} + +static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct sh_vou_file *vou_file = priv; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int i; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + pix->field = V4L2_FIELD_NONE; + + v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1, + &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + + for (i = 0; ARRAY_SIZE(vou_fmt); i++) + if (vou_fmt[i].pfmt == pix->pixelformat) + return 0; + + pix->pixelformat = vou_fmt[0].pfmt; + + return 0; +} + +static int sh_vou_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + return videobuf_reqbufs(&vou_file->vbq, req); +} + +static int sh_vou_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_querybuf(&vou_file->vbq, b); +} + +static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_qbuf(&vou_file->vbq, b); +} + +static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK); +} + +static int sh_vou_streamon(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_file *vou_file = priv; + int ret; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, + video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + /* This calls our .buf_queue() (== sh_vou_buf_queue) */ + return videobuf_streamon(&vou_file->vbq); +} + +static int sh_vou_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + /* + * This calls buf_release from host driver's videobuf_queue_ops for all + * remaining buffers. When the last buffer is freed, stop streaming + */ + videobuf_streamoff(&vou_file->vbq); + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0); + + return 0; +} + +static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) +{ + switch (bus_fmt) { + default: + pr_warning("%s(): Invalid bus-format code %d, using default 8-bit\n", + __func__, bus_fmt); + case SH_VOU_BUS_8BIT: + return 1; + case SH_VOU_BUS_16BIT: + return 0; + case SH_VOU_BUS_BT656: + return 3; + } +} + +static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + int ret; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id); + + if (*std_id & ~vdev->tvnorms) + return -EINVAL; + + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, + s_std_output, *std_id); + /* Shall we continue, if the subdev doesn't support .s_std_output()? */ + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + if (*std_id & V4L2_STD_525_60) + sh_vou_reg_ab_set(vou_dev, VOUCR, + sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29); + else + sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29); + + vou_dev->std = *std_id; + + return 0; +} + +static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + + *std = vou_dev->std; + + return 0; +} + +static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->c = vou_dev->rect; + + return 0; +} + +/* Assume a dull encoder, do all the work ourselves. */ +static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct v4l2_rect *rect = &a->c; + struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; + struct v4l2_pix_format *pix = &vou_dev->pix; + struct sh_vou_geometry geo; + struct v4l2_mbus_framefmt mbfmt = { + /* Revisit: is this the correct code? */ + .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .field = V4L2_FIELD_INTERLACED, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }; + int ret; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__, + rect->width, rect->height, rect->left, rect->top); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1, + &rect->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + + if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH) + rect->left = VOU_MAX_IMAGE_WIDTH - rect->width; + + if (rect->height + rect->top > VOU_MAX_IMAGE_HEIGHT) + rect->top = VOU_MAX_IMAGE_HEIGHT - rect->height; + + geo.output = *rect; + geo.in_width = pix->width; + geo.in_height = pix->height; + + /* Configure the encoder one-to-one, position at 0, ignore errors */ + sd_crop.c.width = geo.output.width; + sd_crop.c.height = geo.output.height; + /* + * We first issue a S_CROP, so that the subsequent S_FMT delivers the + * final encoder configuration. + */ + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, + s_crop, &sd_crop); + mbfmt.width = geo.output.width; + mbfmt.height = geo.output.height; + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, + s_mbus_fmt, &mbfmt); + /* Must be implemented, so, don't check for -ENOIOCTLCMD */ + if (ret < 0) + return ret; + + /* Sanity checks */ + if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH || + (unsigned)mbfmt.height > VOU_MAX_IMAGE_HEIGHT || + mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + return -EIO; + + geo.output.width = mbfmt.width; + geo.output.height = mbfmt.height; + + /* + * No down-scaling. According to the API, current call has precedence: + * http://v4l2spec.bytesex.org/spec/x1904.htm#AEN1954 paragraph two. + */ + vou_adjust_input(&geo, vou_dev->std); + + /* We tried to preserve output rectangle, but it could have changed */ + vou_dev->rect = geo.output; + pix->width = geo.in_width; + pix->height = geo.in_height; + + sh_vou_configure_geometry(vou_dev, vou_dev->pix_idx, + geo.scale_idx_h, geo.scale_idx_v); + + return 0; +} + +/* + * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle + * is the initial register values, height takes the interlaced format into + * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can + * actually only meaningfully contain values <= 720 and <= 240 respectively, and + * not <= 864 and <= 312. + */ +static int sh_vou_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *a) +{ + struct sh_vou_file *vou_file = priv; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = VOU_MAX_IMAGE_WIDTH; + a->bounds.height = VOU_MAX_IMAGE_HEIGHT; + /* Default = max, set VOUDPR = 0, which is not hardware default */ + a->defrect.left = 0; + a->defrect.top = 0; + a->defrect.width = VOU_MAX_IMAGE_WIDTH; + a->defrect.height = VOU_MAX_IMAGE_HEIGHT; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static irqreturn_t sh_vou_isr(int irq, void *dev_id) +{ + struct sh_vou_device *vou_dev = dev_id; + static unsigned long j; + struct videobuf_buffer *vb; + static int cnt; + static int side; + u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked; + u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR); + + if (!(irq_status & 0x300)) { + if (printk_timed_ratelimit(&j, 500)) + dev_warn(vou_dev->v4l2_dev.dev, "IRQ status 0x%x!\n", + irq_status); + return IRQ_NONE; + } + + spin_lock(&vou_dev->lock); + if (!vou_dev->active || list_empty(&vou_dev->queue)) { + if (printk_timed_ratelimit(&j, 500)) + dev_warn(vou_dev->v4l2_dev.dev, + "IRQ without active buffer: %x!\n", irq_status); + /* Just ack: buf_release will disable further interrupts */ + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x300); + spin_unlock(&vou_dev->lock); + return IRQ_HANDLED; + } + + masked = ~(0x300 & irq_status) & irq_status & 0x30304; + dev_dbg(vou_dev->v4l2_dev.dev, + "IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d\n", + irq_status, masked, vou_status, cnt); + + cnt++; + side = vou_status & 0x10000; + + /* Clear only set interrupts */ + sh_vou_reg_a_write(vou_dev, VOUIR, masked); + + vb = vou_dev->active; + list_del(&vb->queue); + + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + + if (list_empty(&vou_dev->queue)) { + /* Stop VOU */ + dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n", + __func__, cnt); + sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); + vou_dev->active = NULL; + vou_dev->status = SH_VOU_INITIALISING; + /* Disable End-of-Frame (VSYNC) interrupts */ + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); + spin_unlock(&vou_dev->lock); + return IRQ_HANDLED; + } + + vou_dev->active = list_entry(vou_dev->queue.next, + struct videobuf_buffer, queue); + + if (vou_dev->active->queue.next != &vou_dev->queue) { + struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next, + struct videobuf_buffer, queue); + sh_vou_schedule_next(vou_dev, new); + } + + spin_unlock(&vou_dev->lock); + + return IRQ_HANDLED; +} + +static int sh_vou_hw_init(struct sh_vou_device *vou_dev) +{ + struct sh_vou_pdata *pdata = vou_dev->pdata; + u32 voucr = sh_vou_ntsc_mode(pdata->bus_fmt) << 29; + int i = 100; + + /* Disable all IRQs */ + sh_vou_reg_a_write(vou_dev, VOUIR, 0); + + /* Reset VOU interfaces - registers unaffected */ + sh_vou_reg_a_write(vou_dev, VOUSRR, 0x101); + while (--i && (sh_vou_reg_a_read(vou_dev, VOUSRR) & 0x101)) + udelay(1); + + if (!i) + return -ETIMEDOUT; + + dev_dbg(vou_dev->v4l2_dev.dev, "Reset took %dus\n", 100 - i); + + if (pdata->flags & SH_VOU_PCLK_FALLING) + voucr |= 1 << 28; + if (pdata->flags & SH_VOU_HSYNC_LOW) + voucr |= 1 << 27; + if (pdata->flags & SH_VOU_VSYNC_LOW) + voucr |= 1 << 26; + sh_vou_reg_ab_set(vou_dev, VOUCR, voucr, 0xfc000000); + + /* Manual register side switching at first */ + sh_vou_reg_a_write(vou_dev, VOURCR, 4); + /* Default - fixed HSYNC length, can be made configurable is required */ + sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000); + + return 0; +} + +/* File operations */ +static int sh_vou_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file), + GFP_KERNEL); + + if (!vou_file) + return -ENOMEM; + + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); + + file->private_data = vou_file; + + if (atomic_inc_return(&vou_dev->use_count) == 1) { + int ret; + /* First open */ + vou_dev->status = SH_VOU_INITIALISING; + pm_runtime_get_sync(vdev->v4l2_dev->dev); + ret = sh_vou_hw_init(vou_dev); + if (ret < 0) { + atomic_dec(&vou_dev->use_count); + pm_runtime_put(vdev->v4l2_dev->dev); + vou_dev->status = SH_VOU_IDLE; + return ret; + } + } + + videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops, + vou_dev->v4l2_dev.dev, &vou_dev->lock, + V4L2_BUF_TYPE_VIDEO_OUTPUT, + V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), vdev); + + return 0; +} + +static int sh_vou_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_file *vou_file = file->private_data; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + if (!atomic_dec_return(&vou_dev->use_count)) { + /* Last close */ + vou_dev->status = SH_VOU_IDLE; + sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101); + pm_runtime_put(vdev->v4l2_dev->dev); + } + + file->private_data = NULL; + kfree(vou_file); + + return 0; +} + +static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sh_vou_file *vou_file = file->private_data; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_mmap_mapper(&vou_file->vbq, vma); +} + +static unsigned int sh_vou_poll(struct file *file, poll_table *wait) +{ + struct sh_vou_file *vou_file = file->private_data; + + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + + return videobuf_poll_stream(file, &vou_file->vbq, wait); +} + +static int sh_vou_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *id) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sh_vou_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg); +} + +static int sh_vou_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct video_device *vdev = video_devdata(file); + struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg); +} +#endif + +/* sh_vou display ioctl operations */ +static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { + .vidioc_querycap = sh_vou_querycap, + .vidioc_enum_fmt_vid_out = sh_vou_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out, + .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out, + .vidioc_reqbufs = sh_vou_reqbufs, + .vidioc_querybuf = sh_vou_querybuf, + .vidioc_qbuf = sh_vou_qbuf, + .vidioc_dqbuf = sh_vou_dqbuf, + .vidioc_streamon = sh_vou_streamon, + .vidioc_streamoff = sh_vou_streamoff, + .vidioc_s_std = sh_vou_s_std, + .vidioc_g_std = sh_vou_g_std, + .vidioc_cropcap = sh_vou_cropcap, + .vidioc_g_crop = sh_vou_g_crop, + .vidioc_s_crop = sh_vou_s_crop, + .vidioc_g_chip_ident = sh_vou_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = sh_vou_g_register, + .vidioc_s_register = sh_vou_s_register, +#endif +}; + +static const struct v4l2_file_operations sh_vou_fops = { + .owner = THIS_MODULE, + .open = sh_vou_open, + .release = sh_vou_release, + .ioctl = video_ioctl2, + .mmap = sh_vou_mmap, + .poll = sh_vou_poll, +}; + +static const struct video_device sh_vou_video_template = { + .name = "sh_vou", + .fops = &sh_vou_fops, + .ioctl_ops = &sh_vou_ioctl_ops, + .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */ + .current_norm = V4L2_STD_NTSC_M, +}; + +static int __devinit sh_vou_probe(struct platform_device *pdev) +{ + struct sh_vou_pdata *vou_pdata = pdev->dev.platform_data; + struct v4l2_rect *rect; + struct v4l2_pix_format *pix; + struct i2c_adapter *i2c_adap; + struct video_device *vdev; + struct sh_vou_device *vou_dev; + struct resource *reg_res, *region; + struct v4l2_subdev *subdev; + int irq, ret; + + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + + if (!vou_pdata || !reg_res || irq <= 0) { + dev_err(&pdev->dev, "Insufficient VOU platform information.\n"); + return -ENODEV; + } + + vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL); + if (!vou_dev) + return -ENOMEM; + + INIT_LIST_HEAD(&vou_dev->queue); + spin_lock_init(&vou_dev->lock); + atomic_set(&vou_dev->use_count, 0); + vou_dev->pdata = vou_pdata; + vou_dev->status = SH_VOU_IDLE; + + rect = &vou_dev->rect; + pix = &vou_dev->pix; + + /* Fill in defaults */ + vou_dev->std = sh_vou_video_template.current_norm; + rect->left = 0; + rect->top = 0; + rect->width = VOU_MAX_IMAGE_WIDTH; + rect->height = VOU_MAX_IMAGE_HEIGHT; + pix->width = VOU_MAX_IMAGE_WIDTH; + pix->height = VOU_MAX_IMAGE_HEIGHT; + pix->pixelformat = V4L2_PIX_FMT_YVYU; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2; + pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * VOU_MAX_IMAGE_HEIGHT; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + + region = request_mem_region(reg_res->start, resource_size(reg_res), + pdev->name); + if (!region) { + dev_err(&pdev->dev, "VOU region already claimed\n"); + ret = -EBUSY; + goto ereqmemreg; + } + + vou_dev->base = ioremap(reg_res->start, resource_size(reg_res)); + if (!vou_dev->base) { + ret = -ENOMEM; + goto emap; + } + + ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev); + if (ret < 0) + goto ereqirq; + + ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev); + if (ret < 0) { + dev_err(&pdev->dev, "Error registering v4l2 device\n"); + goto ev4l2devreg; + } + + /* Allocate memory for video device */ + vdev = video_device_alloc(); + if (vdev == NULL) { + ret = -ENOMEM; + goto evdevalloc; + } + + *vdev = sh_vou_video_template; + if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT) + vdev->tvnorms |= V4L2_STD_PAL; + vdev->v4l2_dev = &vou_dev->v4l2_dev; + vdev->release = video_device_release; + + vou_dev->vdev = vdev; + video_set_drvdata(vdev, vou_dev); + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + i2c_adap = i2c_get_adapter(vou_pdata->i2c_adap); + if (!i2c_adap) { + ret = -ENODEV; + goto ei2cgadap; + } + + ret = sh_vou_hw_init(vou_dev); + if (ret < 0) + goto ereset; + + subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap, + vou_pdata->module_name, vou_pdata->board_info, NULL); + if (!subdev) { + ret = -ENOMEM; + goto ei2cnd; + } + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + goto evregdev; + + return 0; + +evregdev: +ei2cnd: +ereset: + i2c_put_adapter(i2c_adap); +ei2cgadap: + video_device_release(vdev); + pm_runtime_disable(&pdev->dev); +evdevalloc: + v4l2_device_unregister(&vou_dev->v4l2_dev); +ev4l2devreg: + free_irq(irq, vou_dev); +ereqirq: + iounmap(vou_dev->base); +emap: + release_mem_region(reg_res->start, resource_size(reg_res)); +ereqmemreg: + kfree(vou_dev); + return ret; +} + +static int __devexit sh_vou_remove(struct platform_device *pdev) +{ + int irq = platform_get_irq(pdev, 0); + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct sh_vou_device *vou_dev = container_of(v4l2_dev, + struct sh_vou_device, v4l2_dev); + struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next, + struct v4l2_subdev, list); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct resource *reg_res; + + if (irq > 0) + free_irq(irq, vou_dev); + pm_runtime_disable(&pdev->dev); + video_unregister_device(vou_dev->vdev); + i2c_put_adapter(client->adapter); + v4l2_device_unregister(&vou_dev->v4l2_dev); + iounmap(vou_dev->base); + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (reg_res) + release_mem_region(reg_res->start, resource_size(reg_res)); + kfree(vou_dev); + return 0; +} + +static struct platform_driver __refdata sh_vou = { + .remove = __devexit_p(sh_vou_remove), + .driver = { + .name = "sh-vou", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_vou_init(void) +{ + return platform_driver_probe(&sh_vou, sh_vou_probe); +} + +static void __exit sh_vou_exit(void) +{ + platform_driver_unregister(&sh_vou); +} + +module_init(sh_vou_init); +module_exit(sh_vou_exit); + +MODULE_DESCRIPTION("SuperH VOU driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sh-vou"); diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index cbf8087b286f..28e19daadec9 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -2295,7 +2295,7 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { if (ctrl.id == s->qctrl[i].id) { if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) return -EINVAL; @@ -2305,7 +2305,9 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) ctrl.value -= ctrl.value % s->qctrl[i].step; break; } - + } + if (i == ARRAY_SIZE(s->qctrl)) + return -EINVAL; if ((err = s->set_ctrl(cam, &ctrl))) return err; diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index cc40d6ba9f22..522ba3f4c285 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -40,12 +40,12 @@ struct sn9c102_device; static const struct usb_device_id sn9c102_id_table[] = { /* SN9C101 and SN9C102 */ -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ @@ -53,13 +53,13 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ #endif @@ -74,7 +74,7 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, @@ -86,7 +86,7 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, @@ -97,7 +97,7 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, /* SN9C105 */ -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, @@ -121,11 +121,11 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, #endif diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c index db2434948939..2dce5c908c8e 100644 --- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -255,7 +255,7 @@ int sn9c102_probe_hv7131d(struct sn9c102_device* cam) if (err || r0 < 0 || r1 < 0) return -EIO; - if (r0 != 0x00 || r1 != 0x04) + if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04) return -ENODEV; sn9c102_attach_sensor(cam, &hv7131d); diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index a24174ddec46..db1ca0e90d76 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <linux/vmalloc.h> #include <media/soc_camera.h> @@ -388,6 +389,11 @@ static int soc_camera_open(struct file *file) goto eiciadd; } + pm_runtime_enable(&icd->vdev->dev); + ret = pm_runtime_resume(&icd->vdev->dev); + if (ret < 0 && ret != -ENOSYS) + goto eresume; + /* * Try to configure with default parameters. Notice: this is the * very first open, so, we cannot race against other calls, @@ -409,10 +415,12 @@ static int soc_camera_open(struct file *file) return 0; /* - * First five errors are entered with the .video_lock held + * First four errors are entered with the .video_lock held * and use_count == 1 */ esfmt: + pm_runtime_disable(&icd->vdev->dev); +eresume: ici->ops->remove(icd); eiciadd: if (icl->power) @@ -437,7 +445,11 @@ static int soc_camera_close(struct file *file) if (!icd->use_count) { struct soc_camera_link *icl = to_soc_camera_link(icd); + pm_runtime_suspend(&icd->vdev->dev); + pm_runtime_disable(&icd->vdev->dev); + ici->ops->remove(icd); + if (icl->power) icl->power(icd->pdev, 0); } @@ -741,8 +753,7 @@ static int soc_camera_g_crop(struct file *file, void *fh, /* * According to the V4L2 API, drivers shall not update the struct v4l2_crop * argument with the actual geometry, instead, the user shall use G_CROP to - * retrieve it. However, we expect camera host and client drivers to update - * the argument, which we then use internally, but do not return to the user. + * retrieve it. */ static int soc_camera_s_crop(struct file *file, void *fh, struct v4l2_crop *a) @@ -1319,6 +1330,7 @@ static int video_dev_create(struct soc_camera_device *icd) */ static int soc_camera_video_start(struct soc_camera_device *icd) { + struct device_type *type = icd->vdev->dev.type; int ret; if (!icd->dev.parent) @@ -1335,6 +1347,9 @@ static int soc_camera_video_start(struct soc_camera_device *icd) return ret; } + /* Restore device type, possibly set by the subdevice driver */ + icd->vdev->dev.type = type; + return 0; } diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/video/tlg2300/pd-dvb.c index ebd9cb5bec74..edd78f8b1baa 100644 --- a/drivers/media/video/tlg2300/pd-dvb.c +++ b/drivers/media/video/tlg2300/pd-dvb.c @@ -97,15 +97,17 @@ open_out: return ret; } +#ifdef CONFIG_PM static void poseidon_fe_release(struct dvb_frontend *fe) { struct poseidon *pd = fe->demodulator_priv; -#ifdef CONFIG_PM pd->pm_suspend = NULL; pd->pm_resume = NULL; -#endif } +#else +#define poseidon_fe_release NULL +#endif static s32 poseidon_fe_sleep(struct dvb_frontend *fe) { diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c index 2cf0ebf9f28b..c267e0cfb54b 100644 --- a/drivers/media/video/tlg2300/pd-main.c +++ b/drivers/media/video/tlg2300/pd-main.c @@ -24,7 +24,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> @@ -55,8 +54,8 @@ int debug_mode; module_param(debug_mode, int, 0644); MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose"); -const char *firmware_name = "tlg2300_firmware.bin"; -struct usb_driver poseidon_driver; +static const char *firmware_name = "tlg2300_firmware.bin"; +static struct usb_driver poseidon_driver; static LIST_HEAD(pd_device_list); /* @@ -501,7 +500,7 @@ static void poseidon_disconnect(struct usb_interface *interface) kref_put(&pd->kref, poseidon_delete); } -struct usb_driver poseidon_driver = { +static struct usb_driver poseidon_driver = { .name = "poseidon", .probe = poseidon_probe, .disconnect = poseidon_disconnect, diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c index 755766b15157..fae84c2a0c39 100644 --- a/drivers/media/video/tlg2300/pd-radio.c +++ b/drivers/media/video/tlg2300/pd-radio.c @@ -161,7 +161,8 @@ static const struct v4l2_file_operations poseidon_fm_fops = { .ioctl = video_ioctl2, }; -int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *vt) { struct tuner_fm_sig_stat_s fm_stat = {}; int ret, status, count = 5; @@ -203,7 +204,8 @@ int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) return 0; } -int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp) +static int fm_get_freq(struct file *file, void *priv, + struct v4l2_frequency *argp) { struct poseidon *p = file->private_data; @@ -246,7 +248,8 @@ error: return ret; } -int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp) +static int fm_set_freq(struct file *file, void *priv, + struct v4l2_frequency *argp) { struct poseidon *p = file->private_data; @@ -258,13 +261,13 @@ int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp) return set_frequency(p, argp->frequency); } -int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, +static int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *arg) { return 0; } -int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, +static int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, struct v4l2_ext_controls *ctrls) { struct poseidon *p = file->private_data; @@ -285,7 +288,7 @@ int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, return 0; } -int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, +static int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, struct v4l2_ext_controls *ctrls) { int i; @@ -312,13 +315,13 @@ int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, return 0; } -int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, +static int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { return 0; } -int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, +static int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *ctrl) { if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)) @@ -337,7 +340,7 @@ int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, return -EINVAL; } -int tlg_fm_vidioc_querymenu(struct file *file, void *fh, +static int tlg_fm_vidioc_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu) { return v4l2_ctrl_query_menu(qmenu, NULL, NULL); diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index cf8f18c007e6..c750fd115ec4 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -12,11 +12,13 @@ #include "pd-common.h" #include "vendorcmds.h" +#ifdef CONFIG_PM static int pm_video_suspend(struct poseidon *pd); static int pm_video_resume(struct poseidon *pd); +#endif static void iso_bubble_handler(struct work_struct *w); -int usb_transfer_mode; +static int usb_transfer_mode; module_param(usb_transfer_mode, int, 0644); MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous"); @@ -617,7 +619,7 @@ static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, return 0; } -int fire_all_urb(struct video_data *video) +static int fire_all_urb(struct video_data *video) { int i, ret; @@ -877,7 +879,7 @@ out: return ret; } -int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm) { struct front_face *front = fh; logs(front); @@ -1020,7 +1022,7 @@ static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) { a->index = 0; a->capability = V4L2_AUDCAP_STEREO; @@ -1029,7 +1031,7 @@ int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) return 0; } -int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) { return (0 == a->index) ? 0 : -EINVAL; } @@ -1189,7 +1191,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) } /* Just stop the URBs, do not free the URBs */ -int usb_transfer_stop(struct video_data *video) +static int usb_transfer_stop(struct video_data *video) { if (video->is_streaming) { int i; @@ -1518,13 +1520,13 @@ static int pd_video_mmap(struct file *file, struct vm_area_struct *vma) return videobuf_mmap_mapper(&front->q, vma); } -unsigned int pd_video_poll(struct file *file, poll_table *table) +static unsigned int pd_video_poll(struct file *file, poll_table *table) { struct front_face *front = file->private_data; return videobuf_poll_stream(file, &front->q, table); } -ssize_t pd_video_read(struct file *file, char __user *buffer, +static ssize_t pd_video_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct front_face *front = file->private_data; diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index e4815a1806e3..e826114b7fb8 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -79,6 +79,8 @@ struct tvp514x_std_info { }; static struct tvp514x_reg tvp514x_reg_list_default[0x40]; + +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); /** * struct tvp514x_decoder - TVP5146/47 decoder object * @sd: Subdevice Slave handle @@ -644,6 +646,17 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, /* Index out of bound */ return -EINVAL; + /* + * For the sequence streamon -> streamoff and again s_input + * it fails to lock the signal, since streamoff puts TVP514x + * into power off state which leads to failure in sub-sequent s_input. + * + * So power up the TVP514x device here, since it is important to lock + * the signal at this stage. + */ + if (!decoder->streaming) + tvp514x_s_stream(sd, 1); + input_sel = input; output_sel = output; diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 908ffb68e926..47f0582d50a5 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -891,29 +891,26 @@ static int tvp5150_s_routing(struct v4l2_subdev *sd, return 0; } -static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) +{ + /* this is for capturing 36 raw vbi lines + if there's a way to cut off the beginning 2 vbi lines + with the tvp5150 then the vbi line count could be lowered + to 17 lines/field again, although I couldn't find a register + which could do that cropping */ + if (fmt->sample_format == V4L2_PIX_FMT_GREY) + tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); + if (fmt->count[0] == 18 && fmt->count[1] == 18) { + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); + } + return 0; +} + +static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) { - struct v4l2_sliced_vbi_format *svbi; int i; - /* raw vbi */ - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* this is for capturing 36 raw vbi lines - if there's a way to cut off the beginning 2 vbi lines - with the tvp5150 then the vbi line count could be lowered - to 17 lines/field again, although I couldn't find a register - which could do that cropping */ - if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY) - tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); - if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) { - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); - } - return 0; - } - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; if (svbi->service_set != 0) { for (i = 0; i <= 23; i++) { svbi->service_lines[1][i] = 0; @@ -937,14 +934,21 @@ static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } -static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct v4l2_sliced_vbi_format *svbi; - int i, mask = 0; + switch (fmt->type) { + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return tvp5150_s_sliced_fmt(sd, &fmt->fmt.sliced); - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + default: return -EINVAL; - svbi = &fmt->fmt.sliced; + } +} + +static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) +{ + int i, mask = 0; + memset(svbi, 0, sizeof(*svbi)); for (i = 0; i <= 23; i++) { @@ -956,6 +960,12 @@ static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + return tvp5150_g_sliced_fmt(sd, &fmt->fmt.sliced); +} static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) @@ -1046,13 +1056,20 @@ static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .s_routing = tvp5150_s_routing, .g_fmt = tvp5150_g_fmt, .s_fmt = tvp5150_s_fmt, +}; + +static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, + .g_sliced_fmt = tvp5150_g_sliced_fmt, + .s_sliced_fmt = tvp5150_s_sliced_fmt, + .s_raw_fmt = tvp5150_s_raw_fmt, }; static const struct v4l2_subdev_ops tvp5150_ops = { .core = &tvp5150_core_ops, .tuner = &tvp5150_tuner_ops, .video = &tvp5150_video_ops, + .vbi = &tvp5150_vbi_ops, }; diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index 4a69bcc738f3..8085ac392446 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -458,7 +458,7 @@ static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) /* * tvp7002_read - Read a value from a register in an TVP7002 * @sd: ptr to v4l2_subdev struct - * @reg: TVP7002 register address + * @addr: TVP7002 register address * @dst: pointer to 8-bit destination * * Returns value read if successful, or non-zero (-1) otherwise. @@ -488,7 +488,7 @@ static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) * @sd: pointer to standard V4L2 sub-device structure * @reg: destination register * @val: value to be read - * @error: pointer to error value + * @err: pointer to error value * * Read a value in a register and save error value in pointer. * Also update the register table if successful @@ -535,7 +535,7 @@ static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) * @sd: pointer to standard V4L2 sub-device structure * @reg: destination register * @val: value to be written - * @error: pointer to error value + * @err: pointer to error value * * Write a value in a register and save error value in pointer. * Also update the register table if successful @@ -596,7 +596,7 @@ static int tvp7002_write_inittab(struct v4l2_subdev *sd, /* * tvp7002_s_dv_preset() - Set digital video preset * @sd: ptr to v4l2_subdev struct - * @std: ptr to v4l2_dv_preset struct + * @dv_preset: ptr to v4l2_dv_preset struct * * Set the digital video preset for a TVP7002 decoder device. * Returns zero when successful or -EINVAL if register access fails. @@ -676,7 +676,7 @@ static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) /* * tvp7002_queryctrl() - Query a control * @sd: ptr to v4l2_subdev struct - * @ctrl: ptr to v4l2_queryctrl struct + * @qc: ptr to v4l2_queryctrl struct * * Query a control of a TVP7002 decoder device. * Returns zero when successful or -EINVAL if register read fails. @@ -776,7 +776,7 @@ static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) /* * tvp7002_query_dv_preset() - query DV preset * @sd: pointer to standard V4L2 sub-device structure - * @std_id: standard V4L2 v4l2_dv_preset + * @qpreset: standard V4L2 v4l2_dv_preset structure * * Returns the current DV preset by TVP7002. If no active input is * detected, returns -EINVAL @@ -785,7 +785,6 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, struct v4l2_dv_preset *qpreset) { const struct tvp7002_preset_definition *presets = tvp7002_presets; - struct v4l2_dv_enum_preset e_preset; struct tvp7002 *device; u8 progressive; u32 lpfr; @@ -828,20 +827,18 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, } if (index == NUM_PRESETS) { - v4l2_err(sd, "querystd error, lpf = %x, cpl = %x\n", + v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", lpfr, cpln); - return -EINVAL; + /* Could not detect a signal, so return the 'invalid' preset */ + qpreset->preset = V4L2_DV_INVALID; + return 0; } - if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) - return -EINVAL; - /* Set values in found preset */ qpreset->preset = presets->preset; /* Update lines per frame and clocks per line info */ - v4l2_dbg(1, debug, sd, "Current preset: %d %d", - e_preset.width, e_preset.height); + v4l2_dbg(1, debug, sd, "detected preset: %d\n", presets->preset); return 0; } @@ -849,7 +846,7 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, /* * tvp7002_g_register() - Get the value of a register * @sd: ptr to v4l2_subdev struct - * @vreg: ptr to v4l2_dbg_register struct + * @reg: ptr to v4l2_dbg_register struct * * Get the value of a TVP7002 decoder device register. * Returns zero when successful, -EINVAL if register read fails or @@ -876,7 +873,7 @@ static int tvp7002_g_register(struct v4l2_subdev *sd, /* * tvp7002_s_register() - set a control * @sd: ptr to v4l2_subdev struct - * @ctrl: ptr to v4l2_control struct + * @reg: ptr to v4l2_dbg_register struct * * Get the value of a TVP7002 decoder device register. * Returns zero when successful, -EINVAL if register read fails or @@ -899,7 +896,7 @@ static int tvp7002_s_register(struct v4l2_subdev *sd, /* * tvp7002_enum_fmt() - Enum supported formats * @sd: pointer to standard V4L2 sub-device structure - * @enable: pointer to format struct + * @fmtdesc: pointer to format struct * * Enumerate supported formats. */ @@ -994,6 +991,23 @@ static int tvp7002_log_status(struct v4l2_subdev *sd) return 0; } +/* + * tvp7002_enum_dv_presets() - Enum supported digital video formats + * @sd: pointer to standard V4L2 sub-device structure + * @preset: pointer to format struct + * + * Enumerate supported digital video formats. + */ +static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, + struct v4l2_dv_enum_preset *preset) +{ + /* Check requested format index is within range */ + if (preset->index >= NUM_PRESETS) + return -EINVAL; + + return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); +} + /* V4L2 core operation handlers */ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { .g_chip_ident = tvp7002_g_chip_ident, @@ -1009,6 +1023,7 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { /* Specific video subsystem operation handlers */ static const struct v4l2_subdev_video_ops tvp7002_video_ops = { + .enum_dv_presets = tvp7002_enum_dv_presets, .s_dv_preset = tvp7002_s_dv_preset, .query_dv_preset = tvp7002_query_dv_preset, .s_stream = tvp7002_s_stream, @@ -1042,8 +1057,8 @@ static struct tvp7002 tvp7002_dev = { /* * tvp7002_probe - Probe a TVP7002 device - * @sd: ptr to v4l2_subdev struct - * @ctrl: ptr to i2c_device_id struct + * @c: ptr to i2c_client struct + * @id: ptr to i2c_device_id struct * * Initialize the TVP7002 device * Returns zero when successful, -EINVAL if register read fails or diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index fab48ec6c0ea..fbd665fa1979 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -693,12 +693,13 @@ static int qcm_start_data(struct uvd *uvd) static void qcm_stop_data(struct uvd *uvd) { - struct qcm *cam = (struct qcm *) uvd->user_data; + struct qcm *cam; int i, j; int ret; if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) return; + cam = (struct qcm *) uvd->user_data; ret = qcm_camera_off(uvd); if (ret) diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 083765238a6a..42ba28785750 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -244,6 +244,9 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) switch (usbvision_device_data[usbvision->DevModel].Codec) { case CODEC_SAA7113: case CODEC_SAA7111: + /* Without this delay the detection of the saa711x is + hit-and-miss. */ + mdelay(10); v4l2_i2c_new_subdev(&usbvision->v4l2_dev, &usbvision->i2c_adap, "saa7115", "saa7115_auto", 0, saa711x_addrs); diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 7c17ec63c5da..6248a639ba2d 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -137,8 +137,6 @@ static int PowerOnAtOpen = 1; static int video_nr = -1; /* Sequential Number of Radio Device */ static int radio_nr = -1; -/* Sequential Number of VBI Device */ -static int vbi_nr = -1; /* Grab parameters for the device driver */ @@ -148,14 +146,12 @@ module_param(video_debug, int, 0444); module_param(PowerOnAtOpen, int, 0444); module_param(video_nr, int, 0444); module_param(radio_nr, int, 0444); -module_param(vbi_nr, int, 0444); MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device is opened. Default: 1 (On)"); MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)"); // Misc stuff @@ -1244,36 +1240,6 @@ static int usbvision_radio_close(struct file *file) return errCode; } -/* - * Here comes the stuff for vbi on usbvision based devices - * - */ -static int usbvision_vbi_open(struct file *file) -{ - /* TODO */ - return -ENODEV; -} - -static int usbvision_vbi_close(struct file *file) -{ - /* TODO */ - return -ENODEV; -} - -static long usbvision_do_vbi_ioctl(struct file *file, - unsigned int cmd, void *arg) -{ - /* TODO */ - return -ENOIOCTLCMD; -} - -static long usbvision_vbi_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, usbvision_do_vbi_ioctl); -} - - // // Video registration stuff // @@ -1367,21 +1333,6 @@ static struct video_device usbvision_radio_template = { .current_norm = V4L2_STD_PAL }; -// vbi template -static const struct v4l2_file_operations usbvision_vbi_fops = { - .owner = THIS_MODULE, - .open = usbvision_vbi_open, - .release = usbvision_vbi_close, - .ioctl = usbvision_vbi_ioctl, -}; - -static struct video_device usbvision_vbi_template= -{ - .fops = &usbvision_vbi_fops, - .release = video_device_release, - .name = "usbvision-vbi", -}; - static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, struct video_device *vdev_template, @@ -1410,18 +1361,6 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, // unregister video4linux devices static void usbvision_unregister_video(struct usb_usbvision *usbvision) { - // vbi Device: - if (usbvision->vbi) { - PDEBUG(DBG_PROBE, "unregister %s [v4l2]", - video_device_node_name(usbvision->vbi)); - if (video_is_registered(usbvision->vbi)) { - video_unregister_device(usbvision->vbi); - } else { - video_device_release(usbvision->vbi); - } - usbvision->vbi = NULL; - } - // Radio Device: if (usbvision->rdev) { PDEBUG(DBG_PROBE, "unregister %s [v4l2]", @@ -1482,22 +1421,6 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n", usbvision->nr, video_device_node_name(usbvision->rdev)); } - // vbi Device: - if (usbvision_device_data[usbvision->DevModel].vbi) { - usbvision->vbi = usbvision_vdev_init(usbvision, - &usbvision_vbi_template, - "USBVision VBI"); - if (usbvision->vbi == NULL) { - goto err_exit; - } - if (video_register_device(usbvision->vbi, - VFL_TYPE_VBI, - vbi_nr)<0) { - goto err_exit; - } - printk(KERN_INFO "USBVision[%d]: registered USBVision VBI device %s [v4l2] (Not Working Yet!)\n", - usbvision->nr, video_device_node_name(usbvision->vbi)); - } // all done return 0; @@ -1726,8 +1649,6 @@ static int __devinit usbvision_probe(struct usb_interface *intf, usbvision_configure_video(usbvision); mutex_unlock(&usbvision->lock); - - usb_set_intfdata (intf, usbvision); usbvision_create_sysfs(usbvision->vdev); PDEBUG(DBG_PROBE, "success"); @@ -1745,7 +1666,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, */ static void __devexit usbvision_disconnect(struct usb_interface *intf) { - struct usb_usbvision *usbvision = usb_get_intfdata(intf); + struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf)); PDEBUG(DBG_PROBE, ""); @@ -1754,7 +1675,6 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) "%s: usb_get_intfdata() failed\n", __func__); return; } - usb_set_intfdata (intf, NULL); mutex_lock(&usbvision->lock); diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index f8d7458daf3e..d1b3cc0cd87f 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -360,7 +360,6 @@ struct usb_usbvision { struct v4l2_device v4l2_dev; struct video_device *vdev; /* Video Device */ struct video_device *rdev; /* Radio Device */ - struct video_device *vbi; /* VBI Device */ /* i2c Declaration Section*/ struct i2c_adapter i2c_adap; @@ -463,6 +462,11 @@ struct usb_usbvision { int ComprBlockTypes[4]; }; +static inline struct usb_usbvision *to_usbvision(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct usb_usbvision, v4l2_dev); +} + #define call_all(usbvision, o, f, args...) \ v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 6d3850b37161..aa0720af07a0 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -217,8 +217,7 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL, .index = 4, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_RESTORE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_RESTORE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -233,8 +232,9 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_FOCUS_RELATIVE_CONTROL, .index = 6, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN + | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES + | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -249,8 +249,7 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_IRIS_RELATIVE_CONTROL, .index = 8, .size = 1, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -265,8 +264,9 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, .index = 10, .size = 3, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN + | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES + | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -281,8 +281,9 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, .index = 12, .size = 4, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN + | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES + | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -297,8 +298,9 @@ static struct uvc_control_info uvc_ctrls[] = { .selector = UVC_CT_ROLL_RELATIVE_CONTROL, .index = 14, .size = 2, - .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE - | UVC_CONTROL_AUTO_UPDATE, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN + | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES + | UVC_CONTROL_GET_DEF | UVC_CONTROL_AUTO_UPDATE, }, { .entity = UVC_GUID_UVC_CAMERA, @@ -562,6 +564,26 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, { + .id = V4L2_CID_IRIS_ABSOLUTE, + .name = "Iris, Absolute", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_IRIS_RELATIVE, + .name = "Iris, Relative", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_IRIS_RELATIVE_CONTROL, + .size = 8, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { .id = V4L2_CID_ZOOM_ABSOLUTE, .name = "Zoom, Absolute", .entity = UVC_GUID_UVC_CAMERA, @@ -822,6 +844,8 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); v4l2_ctrl->flags = 0; + if (!(ctrl->info->flags & UVC_CONTROL_GET_CUR)) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR)) v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -1047,6 +1071,8 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); step = mapping->get(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + if (step == 0) + step = 1; xctrl->value = min + (xctrl->value - min + step/2) / step * step; xctrl->value = clamp(xctrl->value, min, max); diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 86ff8c12ea58..838b56f097cf 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -91,11 +91,16 @@ static struct uvc_format_desc uvc_fmts[] = { .fcc = V4L2_PIX_FMT_UYVY, }, { - .name = "Greyscale", + .name = "Greyscale (8-bit)", .guid = UVC_GUID_FORMAT_Y800, .fcc = V4L2_PIX_FMT_GREY, }, { + .name = "Greyscale (16-bit)", + .guid = UVC_GUID_FORMAT_Y16, + .fcc = V4L2_PIX_FMT_Y16, + }, + { .name = "RGB Bayer", .guid = UVC_GUID_FORMAT_BY8, .fcc = V4L2_PIX_FMT_SBGGR8, @@ -2105,6 +2110,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Packard Bell EasyNote MX52 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a12, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Syntek (Asus F9SG) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2169,6 +2183,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Arkmicro unbranded */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3290, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_DEF }, /* Bodelin ProScopeHR */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_HI diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 4a925a31b0e0..133c78d113ac 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -388,8 +388,12 @@ unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, poll_wait(file, &buf->wait, wait); if (buf->state == UVC_BUF_STATE_DONE || - buf->state == UVC_BUF_STATE_ERROR) - mask |= POLLIN | POLLRDNORM; + buf->state == UVC_BUF_STATE_ERROR) { + if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + mask |= POLLIN | POLLRDNORM; + else + mask |= POLLOUT | POLLWRNORM; + } done: mutex_unlock(&queue->mutex); diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 2bba059259e6..d1f88406a5e7 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -131,11 +131,13 @@ struct uvc_xu_control { #define UVC_GUID_FORMAT_Y800 \ { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y16 \ + { 'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} #define UVC_GUID_FORMAT_BY8 \ { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} - /* ------------------------------------------------------------------------ * Driver specific constants. */ diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 36b5cb86fb57..4e53b0b3339c 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -51,6 +51,9 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/i2c.h> +#if defined(CONFIG_SPI) +#include <linux/spi/spi.h> +#endif #include <asm/uaccess.h> #include <asm/system.h> #include <asm/pgtable.h> @@ -85,10 +88,9 @@ MODULE_LICENSE("GPL"); val == V4L2_PRIORITY_INTERACTIVE || \ val == V4L2_PRIORITY_RECORD) -int v4l2_prio_init(struct v4l2_prio_state *global) +void v4l2_prio_init(struct v4l2_prio_state *global) { - memset(global,0,sizeof(*global)); - return 0; + memset(global, 0, sizeof(*global)); } EXPORT_SYMBOL(v4l2_prio_init); @@ -108,17 +110,16 @@ int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, } EXPORT_SYMBOL(v4l2_prio_change); -int v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local) +void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local) { - return v4l2_prio_change(global,local,V4L2_PRIORITY_DEFAULT); + v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT); } EXPORT_SYMBOL(v4l2_prio_open); -int v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority *local) +void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local) { - if (V4L2_PRIO_VALID(*local)) - atomic_dec(&global->prios[*local]); - return 0; + if (V4L2_PRIO_VALID(local)) + atomic_dec(&global->prios[local]); } EXPORT_SYMBOL(v4l2_prio_close); @@ -134,11 +135,9 @@ enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global) } EXPORT_SYMBOL(v4l2_prio_max); -int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority *local) +int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local) { - if (*local < v4l2_prio_max(global)) - return -EBUSY; - return 0; + return (local < v4l2_prio_max(global)) ? -EBUSY : 0; } EXPORT_SYMBOL(v4l2_prio_check); @@ -340,6 +339,13 @@ const char **v4l2_ctrl_get_menu(u32 id) "None", "Black & White", "Sepia", + "Negative", + "Emboss", + "Sketch", + "Sky blue", + "Grass green", + "Skin whiten", + "Vivid", NULL }; static const char *tune_preemphasis[] = { @@ -429,10 +435,13 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_SHARPNESS: return "Sharpness"; case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation"; case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; + case V4L2_CID_CHROMA_GAIN: return "Chroma Gain"; case V4L2_CID_COLOR_KILLER: return "Color Killer"; case V4L2_CID_COLORFX: return "Color Effects"; + case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic"; + case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter"; case V4L2_CID_ROTATE: return "Rotate"; - case V4L2_CID_BG_COLOR: return "Background color"; + case V4L2_CID_BG_COLOR: return "Background Color"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -483,6 +492,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic"; + case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute"; + case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; @@ -620,6 +631,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_BLUE_BALANCE: case V4L2_CID_GAMMA: case V4L2_CID_SHARPNESS: + case V4L2_CID_CHROMA_GAIN: case V4L2_CID_RDS_TX_DEVIATION: case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: case V4L2_CID_AUDIO_LIMITER_DEVIATION: @@ -636,6 +648,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_PAN_RELATIVE: case V4L2_CID_TILT_RELATIVE: case V4L2_CID_FOCUS_RELATIVE: + case V4L2_CID_IRIS_RELATIVE: case V4L2_CID_ZOOM_RELATIVE: qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; break; @@ -951,6 +964,66 @@ EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); #endif /* defined(CONFIG_I2C) */ +#if defined(CONFIG_SPI) + +/* Load a spi sub-device. */ + +void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, + const struct v4l2_subdev_ops *ops) +{ + v4l2_subdev_init(sd, ops); + sd->flags |= V4L2_SUBDEV_FL_IS_SPI; + /* the owner is the same as the spi_device's driver owner */ + sd->owner = spi->dev.driver->owner; + /* spi_device and v4l2_subdev point to one another */ + v4l2_set_subdevdata(sd, spi); + spi_set_drvdata(spi, sd); + /* initialize name */ + strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name)); +} +EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init); + +struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, + struct spi_master *master, struct spi_board_info *info) +{ + struct v4l2_subdev *sd = NULL; + struct spi_device *spi = NULL; + + BUG_ON(!v4l2_dev); + + if (info->modalias) + request_module(info->modalias); + + spi = spi_new_device(master, info); + + if (spi == NULL || spi->dev.driver == NULL) + goto error; + + if (!try_module_get(spi->dev.driver->owner)) + goto error; + + sd = spi_get_drvdata(spi); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(v4l2_dev, sd)) + sd = NULL; + + /* Decrease the module use count to match the first try_module_get. */ + module_put(spi->dev.driver->owner); + +error: + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (spi && sd == NULL) + spi_unregister_device(spi); + + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev); + +#endif /* defined(CONFIG_SPI) */ + /* Clamp x to be between min and max, aligned to a multiple of 2^align. min * and max don't have to be aligned, but there must be at least one valid * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index f77f84bfe714..9004a5fe7643 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -1086,6 +1086,9 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_QUERY_DV_PRESET: case VIDIOC_S_DV_TIMINGS: case VIDIOC_G_DV_TIMINGS: + case VIDIOC_DQEVENT: + case VIDIOC_SUBSCRIBE_EVENT: + case VIDIOC_UNSUBSCRIBE_EVENT: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 709069916068..0ca7ec9ca902 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -421,6 +421,10 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, if (!vdev->release) return -EINVAL; + /* v4l2_fh support */ + spin_lock_init(&vdev->fh_lock); + INIT_LIST_HEAD(&vdev->fh_list); + /* Part 1: check device type */ switch (type) { case VFL_TYPE_GRABBER: @@ -596,9 +600,7 @@ void video_unregister_device(struct video_device *vdev) if (!vdev || !video_is_registered(vdev)) return; - mutex_lock(&videodev_lock); clear_bit(V4L2_FL_REGISTERED, &vdev->flags); - mutex_unlock(&videodev_lock); device_unregister(&vdev->dev); } EXPORT_SYMBOL(video_unregister_device); diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 0d06e7cbd5b3..5a7dc4afe92a 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -21,6 +21,9 @@ #include <linux/types.h> #include <linux/ioctl.h> #include <linux/i2c.h> +#if defined(CONFIG_SPI) +#include <linux/spi/spi.h> +#endif #include <linux/videodev2.h> #include <media/v4l2-device.h> @@ -97,6 +100,14 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) i2c_unregister_device(client); } #endif +#if defined(CONFIG_SPI) + if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) { + struct spi_device *spi = v4l2_get_subdevdata(sd); + + if (spi) + spi_unregister_device(spi); + } +#endif } } EXPORT_SYMBOL_GPL(v4l2_device_unregister); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c new file mode 100644 index 000000000000..de74ce07b5e2 --- /dev/null +++ b/drivers/media/video/v4l2-event.c @@ -0,0 +1,292 @@ +/* + * v4l2-event.c + * + * V4L2 events. + * + * Copyright (C) 2009--2010 Nokia Corporation. + * + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> + +#include <linux/sched.h> +#include <linux/slab.h> + +int v4l2_event_init(struct v4l2_fh *fh) +{ + fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL); + if (fh->events == NULL) + return -ENOMEM; + + init_waitqueue_head(&fh->events->wait); + + INIT_LIST_HEAD(&fh->events->free); + INIT_LIST_HEAD(&fh->events->available); + INIT_LIST_HEAD(&fh->events->subscribed); + + fh->events->sequence = -1; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_event_init); + +int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n) +{ + struct v4l2_events *events = fh->events; + unsigned long flags; + + if (!events) { + WARN_ON(1); + return -ENOMEM; + } + + while (events->nallocated < n) { + struct v4l2_kevent *kev; + + kev = kzalloc(sizeof(*kev), GFP_KERNEL); + if (kev == NULL) + return -ENOMEM; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + list_add_tail(&kev->list, &events->free); + events->nallocated++; + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_event_alloc); + +#define list_kfree(list, type, member) \ + while (!list_empty(list)) { \ + type *hi; \ + hi = list_first_entry(list, type, member); \ + list_del(&hi->member); \ + kfree(hi); \ + } + +void v4l2_event_free(struct v4l2_fh *fh) +{ + struct v4l2_events *events = fh->events; + + if (!events) + return; + + list_kfree(&events->free, struct v4l2_kevent, list); + list_kfree(&events->available, struct v4l2_kevent, list); + list_kfree(&events->subscribed, struct v4l2_subscribed_event, list); + + kfree(events); + fh->events = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_event_free); + +static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event) +{ + struct v4l2_events *events = fh->events; + struct v4l2_kevent *kev; + unsigned long flags; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + + if (list_empty(&events->available)) { + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + return -ENOENT; + } + + WARN_ON(events->navailable == 0); + + kev = list_first_entry(&events->available, struct v4l2_kevent, list); + list_move(&kev->list, &events->free); + events->navailable--; + + kev->event.pending = events->navailable; + *event = kev->event; + + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + + return 0; +} + +int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, + int nonblocking) +{ + struct v4l2_events *events = fh->events; + int ret; + + if (nonblocking) + return __v4l2_event_dequeue(fh, event); + + do { + ret = wait_event_interruptible(events->wait, + events->navailable != 0); + if (ret < 0) + return ret; + + ret = __v4l2_event_dequeue(fh, event); + } while (ret == -ENOENT); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_event_dequeue); + +/* Caller must hold fh->event->lock! */ +static struct v4l2_subscribed_event *v4l2_event_subscribed( + struct v4l2_fh *fh, u32 type) +{ + struct v4l2_events *events = fh->events; + struct v4l2_subscribed_event *sev; + + assert_spin_locked(&fh->vdev->fh_lock); + + list_for_each_entry(sev, &events->subscribed, list) { + if (sev->type == type) + return sev; + } + + return NULL; +} + +void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) +{ + struct v4l2_fh *fh; + unsigned long flags; + struct timespec timestamp; + + ktime_get_ts(×tamp); + + spin_lock_irqsave(&vdev->fh_lock, flags); + + list_for_each_entry(fh, &vdev->fh_list, list) { + struct v4l2_events *events = fh->events; + struct v4l2_kevent *kev; + + /* Are we subscribed? */ + if (!v4l2_event_subscribed(fh, ev->type)) + continue; + + /* Increase event sequence number on fh. */ + events->sequence++; + + /* Do we have any free events? */ + if (list_empty(&events->free)) + continue; + + /* Take one and fill it. */ + kev = list_first_entry(&events->free, struct v4l2_kevent, list); + kev->event.type = ev->type; + kev->event.u = ev->u; + kev->event.timestamp = timestamp; + kev->event.sequence = events->sequence; + list_move_tail(&kev->list, &events->available); + + events->navailable++; + + wake_up_all(&events->wait); + } + + spin_unlock_irqrestore(&vdev->fh_lock, flags); +} +EXPORT_SYMBOL_GPL(v4l2_event_queue); + +int v4l2_event_pending(struct v4l2_fh *fh) +{ + return fh->events->navailable; +} +EXPORT_SYMBOL_GPL(v4l2_event_pending); + +int v4l2_event_subscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct v4l2_events *events = fh->events; + struct v4l2_subscribed_event *sev; + unsigned long flags; + + if (fh->events == NULL) { + WARN_ON(1); + return -ENOMEM; + } + + sev = kmalloc(sizeof(*sev), GFP_KERNEL); + if (!sev) + return -ENOMEM; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + + if (v4l2_event_subscribed(fh, sub->type) == NULL) { + INIT_LIST_HEAD(&sev->list); + sev->type = sub->type; + + list_add(&sev->list, &events->subscribed); + sev = NULL; + } + + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + + kfree(sev); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_event_subscribe); + +static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh) +{ + struct v4l2_events *events = fh->events; + struct v4l2_subscribed_event *sev; + unsigned long flags; + + do { + sev = NULL; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + if (!list_empty(&events->subscribed)) { + sev = list_first_entry(&events->subscribed, + struct v4l2_subscribed_event, list); + list_del(&sev->list); + } + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + kfree(sev); + } while (sev); +} + +int v4l2_event_unsubscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct v4l2_subscribed_event *sev; + unsigned long flags; + + if (sub->type == V4L2_EVENT_ALL) { + v4l2_event_unsubscribe_all(fh); + return 0; + } + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + + sev = v4l2_event_subscribed(fh, sub->type); + if (sev != NULL) + list_del(&sev->list); + + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); + + kfree(sev); + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe); diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c new file mode 100644 index 000000000000..d78f184f40c5 --- /dev/null +++ b/drivers/media/video/v4l2-fh.c @@ -0,0 +1,79 @@ +/* + * v4l2-fh.c + * + * V4L2 file handles. + * + * Copyright (C) 2009--2010 Nokia Corporation. + * + * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/bitops.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> + +int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) +{ + fh->vdev = vdev; + INIT_LIST_HEAD(&fh->list); + set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags); + + /* + * fh->events only needs to be initialized if the driver + * supports the VIDIOC_SUBSCRIBE_EVENT ioctl. + */ + if (vdev->ioctl_ops && vdev->ioctl_ops->vidioc_subscribe_event) + return v4l2_event_init(fh); + + fh->events = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_fh_init); + +void v4l2_fh_add(struct v4l2_fh *fh) +{ + unsigned long flags; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + list_add(&fh->list, &fh->vdev->fh_list); + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); +} +EXPORT_SYMBOL_GPL(v4l2_fh_add); + +void v4l2_fh_del(struct v4l2_fh *fh) +{ + unsigned long flags; + + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + list_del_init(&fh->list); + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); +} +EXPORT_SYMBOL_GPL(v4l2_fh_del); + +void v4l2_fh_exit(struct v4l2_fh *fh) +{ + if (fh->vdev == NULL) + return; + + fh->vdev = NULL; + + v4l2_event_free(fh); +} +EXPORT_SYMBOL_GPL(v4l2_fh_exit); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 7d59c107f13b..0eeceae50329 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -26,6 +26,8 @@ #endif #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include <media/v4l2-chip-ident.h> #define dbgarg(cmd, fmt, arg...) \ @@ -291,6 +293,9 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_QUERY_DV_PRESET)] = "VIDIOC_QUERY_DV_PRESET", [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS", [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS", + [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT", + [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT", + [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT", }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -610,17 +615,33 @@ static long __video_do_ioctl(struct file *file, void *fh = file->private_data; long ret = -EINVAL; + if (ops == NULL) { + printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n", + vfd->name); + return -EINVAL; + } + +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /******************************************************** + All other V4L1 calls are handled by v4l1_compat module. + Those calls will be translated into V4L2 calls, and + __video_do_ioctl will be called again, with one or more + V4L2 ioctls. + ********************************************************/ + if (_IOC_TYPE(cmd) == 'v' && cmd != VIDIOCGMBUF && + _IOC_NR(cmd) < BASE_VIDIOCPRIVATE) { + return v4l_compat_translate_ioctl(file, cmd, arg, + __video_do_ioctl); + } +#endif + if ((vfd->debug & V4L2_DEBUG_IOCTL) && !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { v4l_print_ioctl(vfd->name, cmd); printk(KERN_CONT "\n"); } - if (ops == NULL) { - printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n", - vfd->name); - return -EINVAL; - } + switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT /*********************************************************** @@ -630,31 +651,21 @@ static long __video_do_ioctl(struct file *file, ***********************************************************/ /* --- streaming capture ------------------------------------- */ - if (cmd == VIDIOCGMBUF) { + case VIDIOCGMBUF: + { struct video_mbuf *p = arg; if (!ops->vidiocgmbuf) - return ret; + break; ret = ops->vidiocgmbuf(file, fh, p); if (!ret) dbgarg(cmd, "size=%d, frames=%d, offsets=0x%08lx\n", p->size, p->frames, (unsigned long)p->offsets); - return ret; + break; } - - /******************************************************** - All other V4L1 calls are handled by v4l1_compat module. - Those calls will be translated into V4L2 calls, and - __video_do_ioctl will be called again, with one or more - V4L2 ioctls. - ********************************************************/ - if (_IOC_TYPE(cmd) == 'v' && _IOC_NR(cmd) < BASE_VIDIOCPRIVATE) - return v4l_compat_translate_ioctl(file, cmd, arg, - __video_do_ioctl); #endif - switch (cmd) { /* --- capabilities ------------------------------------------ */ case VIDIOC_QUERYCAP: { @@ -1072,7 +1083,7 @@ static long __video_do_ioctl(struct file *file, id &= ~curr_id; } if (i <= index) - return -EINVAL; + break; v4l2_video_std_construct(p, curr_id, descr); @@ -1597,7 +1608,7 @@ static long __video_do_ioctl(struct file *file, v4l2_std_id std = vfd->current_norm; if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + break; ret = 0; if (ops->vidioc_g_std) @@ -1942,7 +1953,55 @@ static long __video_do_ioctl(struct file *file, } break; } + case VIDIOC_DQEVENT: + { + struct v4l2_event *ev = arg; + + if (!ops->vidioc_subscribe_event) + break; + ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK); + if (ret < 0) { + dbgarg(cmd, "no pending events?"); + break; + } + dbgarg(cmd, + "pending=%d, type=0x%8.8x, sequence=%d, " + "timestamp=%lu.%9.9lu ", + ev->pending, ev->type, ev->sequence, + ev->timestamp.tv_sec, ev->timestamp.tv_nsec); + break; + } + case VIDIOC_SUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + if (!ops->vidioc_subscribe_event) + break; + + ret = ops->vidioc_subscribe_event(fh, sub); + if (ret < 0) { + dbgarg(cmd, "failed, ret=%ld", ret); + break; + } + dbgarg(cmd, "type=0x%8.8x", sub->type); + break; + } + case VIDIOC_UNSUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + if (!ops->vidioc_unsubscribe_event) + break; + + ret = ops->vidioc_unsubscribe_event(fh, sub); + if (ret < 0) { + dbgarg(cmd, "failed, ret=%ld", ret); + break; + } + dbgarg(cmd, "type=0x%8.8x", sub->type); + break; + } default: { if (!ops->vidioc_default) @@ -2006,7 +2065,7 @@ long video_ioctl2(struct file *file, { char sbuf[128]; void *mbuf = NULL; - void *parg = NULL; + void *parg = (void *)arg; long err = -EINVAL; int is_ext_ctrl; size_t ctrls_size = 0; diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c new file mode 100644 index 000000000000..f45f9405ea39 --- /dev/null +++ b/drivers/media/video/v4l2-mem2mem.c @@ -0,0 +1,633 @@ +/* + * Memory-to-memory device framework for Video for Linux 2 and videobuf. + * + * Helper functions for devices that use videobuf buffers for both their + * source and destination. + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <p.osciak@samsung.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + * + * 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. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <media/videobuf-core.h> +#include <media/v4l2-mem2mem.h> + +MODULE_DESCRIPTION("Mem to mem device framework for videobuf"); +MODULE_AUTHOR("Pawel Osciak, <p.osciak@samsung.com>"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); + +#define dprintk(fmt, arg...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\ + } while (0) + + +/* Instance is already queued on the job_queue */ +#define TRANS_QUEUED (1 << 0) +/* Instance is currently running in hardware */ +#define TRANS_RUNNING (1 << 1) + + +/* Offset base for buffers on the destination queue - used to distinguish + * between source and destination buffers when mmapping - they receive the same + * offsets but for different queues */ +#define DST_QUEUE_OFF_BASE (1 << 30) + + +/** + * struct v4l2_m2m_dev - per-device context + * @curr_ctx: currently running instance + * @job_queue: instances queued to run + * @job_spinlock: protects job_queue + * @m2m_ops: driver callbacks + */ +struct v4l2_m2m_dev { + struct v4l2_m2m_ctx *curr_ctx; + + struct list_head job_queue; + spinlock_t job_spinlock; + + struct v4l2_m2m_ops *m2m_ops; +}; + +static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &m2m_ctx->cap_q_ctx; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &m2m_ctx->out_q_ctx; + default: + printk(KERN_ERR "Invalid buffer type\n"); + return NULL; + } +} + +/** + * v4l2_m2m_get_vq() - return videobuf_queue for the given type + */ +struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type) +{ + struct v4l2_m2m_queue_ctx *q_ctx; + + q_ctx = get_queue_ctx(m2m_ctx, type); + if (!q_ctx) + return NULL; + + return &q_ctx->q; +} +EXPORT_SYMBOL(v4l2_m2m_get_vq); + +/** + * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers + */ +void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) +{ + struct v4l2_m2m_queue_ctx *q_ctx; + struct videobuf_buffer *vb = NULL; + unsigned long flags; + + q_ctx = get_queue_ctx(m2m_ctx, type); + if (!q_ctx) + return NULL; + + spin_lock_irqsave(q_ctx->q.irqlock, flags); + + if (list_empty(&q_ctx->rdy_queue)) + goto end; + + vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, queue); + vb->state = VIDEOBUF_ACTIVE; + +end: + spin_unlock_irqrestore(q_ctx->q.irqlock, flags); + return vb; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); + +/** + * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and + * return it + */ +void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type) +{ + struct v4l2_m2m_queue_ctx *q_ctx; + struct videobuf_buffer *vb = NULL; + unsigned long flags; + + q_ctx = get_queue_ctx(m2m_ctx, type); + if (!q_ctx) + return NULL; + + spin_lock_irqsave(q_ctx->q.irqlock, flags); + if (!list_empty(&q_ctx->rdy_queue)) { + vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, + queue); + list_del(&vb->queue); + q_ctx->num_rdy--; + } + spin_unlock_irqrestore(q_ctx->q.irqlock, flags); + + return vb; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove); + +/* + * Scheduling handlers + */ + +/** + * v4l2_m2m_get_curr_priv() - return driver private data for the currently + * running instance or NULL if no instance is running + */ +void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev) +{ + unsigned long flags; + void *ret = NULL; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (m2m_dev->curr_ctx) + ret = m2m_dev->curr_ctx->priv; + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + return ret; +} +EXPORT_SYMBOL(v4l2_m2m_get_curr_priv); + +/** + * v4l2_m2m_try_run() - select next job to perform and run it if possible + * + * Get next transaction (if present) from the waiting jobs list and run it. + */ +static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (NULL != m2m_dev->curr_ctx) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("Another instance is running, won't run now\n"); + return; + } + + if (list_empty(&m2m_dev->job_queue)) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("No job pending\n"); + return; + } + + m2m_dev->curr_ctx = list_entry(m2m_dev->job_queue.next, + struct v4l2_m2m_ctx, queue); + m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING; + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv); +} + +/** + * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to + * the pending job queue and add it if so. + * @m2m_ctx: m2m context assigned to the instance to be checked + * + * There are three basic requirements an instance has to meet to be able to run: + * 1) at least one source buffer has to be queued, + * 2) at least one destination buffer has to be queued, + * 3) streaming has to be on. + * + * There may also be additional, custom requirements. In such case the driver + * should supply a custom callback (job_ready in v4l2_m2m_ops) that should + * return 1 if the instance is ready. + * An example of the above could be an instance that requires more than one + * src/dst buffer per transaction. + */ +static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct v4l2_m2m_dev *m2m_dev; + unsigned long flags_job, flags; + + m2m_dev = m2m_ctx->m2m_dev; + dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx); + + if (!m2m_ctx->out_q_ctx.q.streaming + || !m2m_ctx->cap_q_ctx.q.streaming) { + dprintk("Streaming needs to be on for both queues\n"); + return; + } + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job); + if (m2m_ctx->job_flags & TRANS_QUEUED) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + dprintk("On job queue already\n"); + return; + } + + spin_lock_irqsave(m2m_ctx->out_q_ctx.q.irqlock, flags); + if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) { + spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + dprintk("No input buffers available\n"); + return; + } + if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) { + spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + dprintk("No output buffers available\n"); + return; + } + spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags); + + if (m2m_dev->m2m_ops->job_ready + && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + dprintk("Driver not ready\n"); + return; + } + + list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue); + m2m_ctx->job_flags |= TRANS_QUEUED; + + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + + v4l2_m2m_try_run(m2m_dev); +} + +/** + * v4l2_m2m_job_finish() - inform the framework that a job has been finished + * and have it clean up + * + * Called by a driver to yield back the device after it has finished with it. + * Should be called as soon as possible after reaching a state which allows + * other instances to take control of the device. + * + * This function has to be called only after device_run() callback has been + * called on the driver. To prevent recursion, it should not be called directly + * from the device_run() callback though. + */ +void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("Called by an instance not currently running\n"); + return; + } + + list_del(&m2m_dev->curr_ctx->queue); + m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); + m2m_dev->curr_ctx = NULL; + + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + /* This instance might have more buffers ready, but since we do not + * allow more than one job on the job_queue per instance, each has + * to be scheduled separately after the previous one finishes. */ + v4l2_m2m_try_schedule(m2m_ctx); + v4l2_m2m_try_run(m2m_dev); +} +EXPORT_SYMBOL(v4l2_m2m_job_finish); + +/** + * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer + */ +int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_requestbuffers *reqbufs) +{ + struct videobuf_queue *vq; + + vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type); + return videobuf_reqbufs(vq, reqbufs); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); + +/** + * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer + * + * See v4l2_m2m_mmap() documentation for details. + */ +int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) +{ + struct videobuf_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + ret = videobuf_querybuf(vq, buf); + + if (buf->memory == V4L2_MEMORY_MMAP + && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + buf->m.offset += DST_QUEUE_OFF_BASE; + } + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); + +/** + * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on + * the type + */ +int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) +{ + struct videobuf_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + ret = videobuf_qbuf(vq, buf); + if (!ret) + v4l2_m2m_try_schedule(m2m_ctx); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf); + +/** + * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on + * the type + */ +int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) +{ + struct videobuf_queue *vq; + + vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); + return videobuf_dqbuf(vq, buf, file->f_flags & O_NONBLOCK); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); + +/** + * v4l2_m2m_streamon() - turn on streaming for a video queue + */ +int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type) +{ + struct videobuf_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(m2m_ctx, type); + ret = videobuf_streamon(vq); + if (!ret) + v4l2_m2m_try_schedule(m2m_ctx); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_streamon); + +/** + * v4l2_m2m_streamoff() - turn off streaming for a video queue + */ +int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type) +{ + struct videobuf_queue *vq; + + vq = v4l2_m2m_get_vq(m2m_ctx, type); + return videobuf_streamoff(vq); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff); + +/** + * v4l2_m2m_poll() - poll replacement, for destination buffers only + * + * Call from the driver's poll() function. Will poll both queues. If a buffer + * is available to dequeue (with dqbuf) from the source queue, this will + * indicate that a non-blocking write can be performed, while read will be + * returned in case of the destination queue. + */ +unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct poll_table_struct *wait) +{ + struct videobuf_queue *src_q, *dst_q; + struct videobuf_buffer *src_vb = NULL, *dst_vb = NULL; + unsigned int rc = 0; + + src_q = v4l2_m2m_get_src_vq(m2m_ctx); + dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); + + mutex_lock(&src_q->vb_lock); + mutex_lock(&dst_q->vb_lock); + + if (src_q->streaming && !list_empty(&src_q->stream)) + src_vb = list_first_entry(&src_q->stream, + struct videobuf_buffer, stream); + if (dst_q->streaming && !list_empty(&dst_q->stream)) + dst_vb = list_first_entry(&dst_q->stream, + struct videobuf_buffer, stream); + + if (!src_vb && !dst_vb) { + rc = POLLERR; + goto end; + } + + if (src_vb) { + poll_wait(file, &src_vb->done, wait); + if (src_vb->state == VIDEOBUF_DONE + || src_vb->state == VIDEOBUF_ERROR) + rc |= POLLOUT | POLLWRNORM; + } + if (dst_vb) { + poll_wait(file, &dst_vb->done, wait); + if (dst_vb->state == VIDEOBUF_DONE + || dst_vb->state == VIDEOBUF_ERROR) + rc |= POLLIN | POLLRDNORM; + } + +end: + mutex_unlock(&dst_q->vb_lock); + mutex_unlock(&src_q->vb_lock); + return rc; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_poll); + +/** + * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer + * + * Call from driver's mmap() function. Will handle mmap() for both queues + * seamlessly for videobuffer, which will receive normal per-queue offsets and + * proper videobuf queue pointers. The differentiation is made outside videobuf + * by adding a predefined offset to buffers from one of the queues and + * subtracting it before passing it back to videobuf. Only drivers (and + * thus applications) receive modified offsets. + */ +int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct vm_area_struct *vma) +{ + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct videobuf_queue *vq; + + if (offset < DST_QUEUE_OFF_BASE) { + vq = v4l2_m2m_get_src_vq(m2m_ctx); + } else { + vq = v4l2_m2m_get_dst_vq(m2m_ctx); + vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); + } + + return videobuf_mmap_mapper(vq, vma); +} +EXPORT_SYMBOL(v4l2_m2m_mmap); + +/** + * v4l2_m2m_init() - initialize per-driver m2m data + * + * Usually called from driver's probe() function. + */ +struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops) +{ + struct v4l2_m2m_dev *m2m_dev; + + if (!m2m_ops) + return ERR_PTR(-EINVAL); + + BUG_ON(!m2m_ops->device_run); + BUG_ON(!m2m_ops->job_abort); + + m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL); + if (!m2m_dev) + return ERR_PTR(-ENOMEM); + + m2m_dev->curr_ctx = NULL; + m2m_dev->m2m_ops = m2m_ops; + INIT_LIST_HEAD(&m2m_dev->job_queue); + spin_lock_init(&m2m_dev->job_spinlock); + + return m2m_dev; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_init); + +/** + * v4l2_m2m_release() - cleans up and frees a m2m_dev structure + * + * Usually called from driver's remove() function. + */ +void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev) +{ + kfree(m2m_dev); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_release); + +/** + * v4l2_m2m_ctx_init() - allocate and initialize a m2m context + * @priv - driver's instance private data + * @m2m_dev - a previously initialized m2m_dev struct + * @vq_init - a callback for queue type-specific initialization function to be + * used for initializing videobuf_queues + * + * Usually called from driver's open() function. + */ +struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev, + void (*vq_init)(void *priv, struct videobuf_queue *, + enum v4l2_buf_type)) +{ + struct v4l2_m2m_ctx *m2m_ctx; + struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx; + + if (!vq_init) + return ERR_PTR(-EINVAL); + + m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL); + if (!m2m_ctx) + return ERR_PTR(-ENOMEM); + + m2m_ctx->priv = priv; + m2m_ctx->m2m_dev = m2m_dev; + + out_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + cap_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + INIT_LIST_HEAD(&out_q_ctx->rdy_queue); + INIT_LIST_HEAD(&cap_q_ctx->rdy_queue); + + INIT_LIST_HEAD(&m2m_ctx->queue); + + vq_init(priv, &out_q_ctx->q, V4L2_BUF_TYPE_VIDEO_OUTPUT); + vq_init(priv, &cap_q_ctx->q, V4L2_BUF_TYPE_VIDEO_CAPTURE); + out_q_ctx->q.priv_data = cap_q_ctx->q.priv_data = priv; + + return m2m_ctx; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init); + +/** + * v4l2_m2m_ctx_release() - release m2m context + * + * Usually called from driver's release() function. + */ +void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct v4l2_m2m_dev *m2m_dev; + struct videobuf_buffer *vb; + unsigned long flags; + + m2m_dev = m2m_ctx->m2m_dev; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + if (m2m_ctx->job_flags & TRANS_RUNNING) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); + dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); + vb = v4l2_m2m_next_dst_buf(m2m_ctx); + BUG_ON(NULL == vb); + wait_event(vb->done, vb->state != VIDEOBUF_ACTIVE + && vb->state != VIDEOBUF_QUEUED); + } else if (m2m_ctx->job_flags & TRANS_QUEUED) { + list_del(&m2m_ctx->queue); + m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("m2m_ctx: %p had been on queue and was removed\n", + m2m_ctx); + } else { + /* Do nothing, was not on queue/running */ + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + } + + videobuf_stop(&m2m_ctx->cap_q_ctx.q); + videobuf_stop(&m2m_ctx->out_q_ctx.q); + + videobuf_mmap_free(&m2m_ctx->cap_q_ctx.q); + videobuf_mmap_free(&m2m_ctx->out_q_ctx.q); + + kfree(m2m_ctx); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release); + +/** + * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list. + * + * Call from buf_queue(), videobuf_queue_ops callback. + * + * Locking: Caller holds q->irqlock (taken by videobuf before calling buf_queue + * callback in the driver). + */ +void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct v4l2_m2m_queue_ctx *q_ctx; + + q_ctx = get_queue_ctx(m2m_ctx, vq->type); + if (!q_ctx) + return; + + list_add_tail(&vb->queue, &q_ctx->rdy_queue); + q_ctx->num_rdy++; + + vb->state = VIDEOBUF_QUEUED; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); + diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index bb0a1c8de414..7d3378437ded 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -24,10 +24,15 @@ #include <media/videobuf-core.h> #define MAGIC_BUFFER 0x20070728 -#define MAGIC_CHECK(is, should) do { \ - if (unlikely((is) != (should))) { \ - printk(KERN_ERR "magic mismatch: %x (expected %x)\n", is, should); \ - BUG(); } } while (0) +#define MAGIC_CHECK(is, should) \ + do { \ + if (unlikely((is) != (should))) { \ + printk(KERN_ERR \ + "magic mismatch: %x (expected %x)\n", \ + is, should); \ + BUG(); \ + } \ + } while (0) static int debug; module_param(debug, int, 0644); @@ -36,16 +41,18 @@ MODULE_DESCRIPTION("helper module to manage video4linux buffers"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); -#define dprintk(level, fmt, arg...) do { \ - if (debug >= level) \ - printk(KERN_DEBUG "vbuf: " fmt , ## arg); } while (0) +#define dprintk(level, fmt, arg...) \ + do { \ + if (debug >= level) \ + printk(KERN_DEBUG "vbuf: " fmt, ## arg); \ + } while (0) /* --------------------------------------------------------------------- */ #define CALL(q, f, arg...) \ ((q->int_ops->f) ? q->int_ops->f(arg) : 0) -void *videobuf_alloc(struct videobuf_queue *q) +struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q) { struct videobuf_buffer *vb; @@ -57,14 +64,14 @@ void *videobuf_alloc(struct videobuf_queue *q) } vb = q->int_ops->alloc(q->msize); - if (NULL != vb) { init_waitqueue_head(&vb->done); - vb->magic = MAGIC_BUFFER; + vb->magic = MAGIC_BUFFER; } return vb; } +EXPORT_SYMBOL_GPL(videobuf_alloc); #define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\ vb->state != VIDEOBUF_QUEUED) @@ -86,6 +93,7 @@ int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr) return 0; } +EXPORT_SYMBOL_GPL(videobuf_waiton); int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf) @@ -95,16 +103,16 @@ int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, return CALL(q, iolock, q, vb, fbuf); } +EXPORT_SYMBOL_GPL(videobuf_iolock); -void *videobuf_queue_to_vmalloc (struct videobuf_queue *q, - struct videobuf_buffer *buf) +void *videobuf_queue_to_vaddr(struct videobuf_queue *q, + struct videobuf_buffer *buf) { - if (q->int_ops->vmalloc) - return q->int_ops->vmalloc(buf); - else - return NULL; + if (q->int_ops->vaddr) + return q->int_ops->vaddr(buf); + return NULL; } -EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc); +EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr); /* --------------------------------------------------------------------- */ @@ -146,6 +154,7 @@ void videobuf_queue_core_init(struct videobuf_queue *q, init_waitqueue_head(&q->wait); INIT_LIST_HEAD(&q->stream); } +EXPORT_SYMBOL_GPL(videobuf_queue_core_init); /* Locking: Only usage in bttv unsafe find way to remove */ int videobuf_queue_is_busy(struct videobuf_queue *q) @@ -184,6 +193,7 @@ int videobuf_queue_is_busy(struct videobuf_queue *q) } return 0; } +EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); /* Locking: Caller holds q->vb_lock */ void videobuf_queue_cancel(struct videobuf_queue *q) @@ -216,6 +226,7 @@ void videobuf_queue_cancel(struct videobuf_queue *q) } INIT_LIST_HEAD(&q->stream); } +EXPORT_SYMBOL_GPL(videobuf_queue_cancel); /* --------------------------------------------------------------------- */ @@ -237,6 +248,7 @@ enum v4l2_field videobuf_next_field(struct videobuf_queue *q) } return field; } +EXPORT_SYMBOL_GPL(videobuf_next_field); /* Locking: Caller holds q->vb_lock */ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, @@ -273,8 +285,10 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, case VIDEOBUF_ACTIVE: b->flags |= V4L2_BUF_FLAG_QUEUED; break; - case VIDEOBUF_DONE: case VIDEOBUF_ERROR: + b->flags |= V4L2_BUF_FLAG_ERROR; + /* fall through */ + case VIDEOBUF_DONE: b->flags |= V4L2_BUF_FLAG_DONE; break; case VIDEOBUF_NEEDS_INIT: @@ -298,20 +312,15 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, static int __videobuf_mmap_free(struct videobuf_queue *q) { int i; - int rc; if (!q) return 0; MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - rc = CALL(q, mmap_free, q); - - q->is_mmapped = 0; - - if (rc < 0) - return rc; + for (i = 0; i < VIDEO_MAX_FRAME; i++) + if (q->bufs[i] && q->bufs[i]->map) + return -EBUSY; for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) @@ -321,7 +330,7 @@ static int __videobuf_mmap_free(struct videobuf_queue *q) q->bufs[i] = NULL; } - return rc; + return 0; } int videobuf_mmap_free(struct videobuf_queue *q) @@ -332,6 +341,7 @@ int videobuf_mmap_free(struct videobuf_queue *q) mutex_unlock(&q->vb_lock); return ret; } +EXPORT_SYMBOL_GPL(videobuf_mmap_free); /* Locking: Caller holds q->vb_lock */ int __videobuf_mmap_setup(struct videobuf_queue *q, @@ -351,7 +361,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, for (i = 0; i < bcount; i++) { q->bufs[i] = videobuf_alloc(q); - if (q->bufs[i] == NULL) + if (NULL == q->bufs[i]) break; q->bufs[i]->i = i; @@ -372,11 +382,11 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, if (!i) return -ENOMEM; - dprintk(1, "mmap setup: %d buffers, %d bytes each\n", - i, bsize); + dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize); return i; } +EXPORT_SYMBOL_GPL(__videobuf_mmap_setup); int videobuf_mmap_setup(struct videobuf_queue *q, unsigned int bcount, unsigned int bsize, @@ -388,6 +398,7 @@ int videobuf_mmap_setup(struct videobuf_queue *q, mutex_unlock(&q->vb_lock); return ret; } +EXPORT_SYMBOL_GPL(videobuf_mmap_setup); int videobuf_reqbufs(struct videobuf_queue *q, struct v4l2_requestbuffers *req) @@ -432,7 +443,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, q->ops->buf_setup(q, &count, &size); dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n", count, size, - (unsigned int)((count*PAGE_ALIGN(size))>>PAGE_SHIFT) ); + (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT)); retval = __videobuf_mmap_setup(q, count, size, req->memory); if (retval < 0) { @@ -447,6 +458,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_reqbufs); int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) { @@ -473,9 +485,9 @@ done: mutex_unlock(&q->vb_lock); return ret; } +EXPORT_SYMBOL_GPL(videobuf_querybuf); -int videobuf_qbuf(struct videobuf_queue *q, - struct v4l2_buffer *b) +int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) { struct videobuf_buffer *buf; enum v4l2_field field; @@ -534,6 +546,13 @@ int videobuf_qbuf(struct videobuf_queue *q, "but buffer addr is zero!\n"); goto done; } + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT + || q->type == V4L2_BUF_TYPE_VBI_OUTPUT + || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + buf->size = b->bytesused; + buf->field = b->field; + buf->ts = b->timestamp; + } break; case V4L2_MEMORY_USERPTR: if (b->length < buf->bsize) { @@ -567,11 +586,11 @@ int videobuf_qbuf(struct videobuf_queue *q, q->ops->buf_queue(q, buf); spin_unlock_irqrestore(q->irqlock, flags); } - dprintk(1, "qbuf: succeded\n"); + dprintk(1, "qbuf: succeeded\n"); retval = 0; wake_up_interruptible_sync(&q->wait); - done: +done: mutex_unlock(&q->vb_lock); if (b->memory == V4L2_MEMORY_MMAP) @@ -579,7 +598,7 @@ int videobuf_qbuf(struct videobuf_queue *q, return retval; } - +EXPORT_SYMBOL_GPL(videobuf_qbuf); /* Locking: Caller holds q->vb_lock */ static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock) @@ -624,7 +643,6 @@ done: return retval; } - /* Locking: Caller holds q->vb_lock */ static int stream_next_buffer(struct videobuf_queue *q, struct videobuf_buffer **vb, int nonblocking) @@ -647,13 +665,14 @@ done: } int videobuf_dqbuf(struct videobuf_queue *q, - struct v4l2_buffer *b, int nonblocking) + struct v4l2_buffer *b, int nonblocking) { struct videobuf_buffer *buf = NULL; int retval; MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); + memset(b, 0, sizeof(*b)); mutex_lock(&q->vb_lock); retval = stream_next_buffer(q, &buf, nonblocking); @@ -665,28 +684,25 @@ int videobuf_dqbuf(struct videobuf_queue *q, switch (buf->state) { case VIDEOBUF_ERROR: dprintk(1, "dqbuf: state is error\n"); - retval = -EIO; - CALL(q, sync, q, buf); - buf->state = VIDEOBUF_IDLE; break; case VIDEOBUF_DONE: dprintk(1, "dqbuf: state is done\n"); - CALL(q, sync, q, buf); - buf->state = VIDEOBUF_IDLE; break; default: dprintk(1, "dqbuf: state invalid\n"); retval = -EINVAL; goto done; } - list_del(&buf->stream); - memset(b, 0, sizeof(*b)); + CALL(q, sync, q, buf); videobuf_status(q, b, buf, q->type); - - done: + list_del(&buf->stream); + buf->state = VIDEOBUF_IDLE; + b->flags &= ~V4L2_BUF_FLAG_DONE; +done: mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_dqbuf); int videobuf_streamon(struct videobuf_queue *q) { @@ -709,10 +725,11 @@ int videobuf_streamon(struct videobuf_queue *q) spin_unlock_irqrestore(q->irqlock, flags); wake_up_interruptible_sync(&q->wait); - done: +done: mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_streamon); /* Locking: Caller holds q->vb_lock */ static int __videobuf_streamoff(struct videobuf_queue *q) @@ -735,6 +752,7 @@ int videobuf_streamoff(struct videobuf_queue *q) return retval; } +EXPORT_SYMBOL_GPL(videobuf_streamoff); /* Locking: Caller holds q->vb_lock */ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, @@ -774,7 +792,7 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, retval = q->read_buf->size; } - done: +done: /* cleanup */ q->ops->buf_release(q, q->read_buf); kfree(q->read_buf); @@ -782,6 +800,49 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, return retval; } +static int __videobuf_copy_to_user(struct videobuf_queue *q, + struct videobuf_buffer *buf, + char __user *data, size_t count, + int nonblocking) +{ + void *vaddr = CALL(q, vaddr, buf); + + /* copy to userspace */ + if (count > buf->size - q->read_off) + count = buf->size - q->read_off; + + if (copy_to_user(data, vaddr + q->read_off, count)) + return -EFAULT; + + return count; +} + +static int __videobuf_copy_stream(struct videobuf_queue *q, + struct videobuf_buffer *buf, + char __user *data, size_t count, size_t pos, + int vbihack, int nonblocking) +{ + unsigned int *fc = CALL(q, vaddr, buf); + + if (vbihack) { + /* dirty, undocumented hack -- pass the frame counter + * within the last four bytes of each vbi data block. + * We need that one to maintain backward compatibility + * to all vbi decoding software out there ... */ + fc += (buf->size >> 2) - 1; + *fc = buf->field_count >> 1; + dprintk(1, "vbihack: %d\n", *fc); + } + + /* copy stuff using the common method */ + count = __videobuf_copy_to_user(q, buf, data, count, nonblocking); + + if ((count == -EFAULT) && (pos == 0)) + return -EFAULT; + + return count; +} + ssize_t videobuf_read_one(struct videobuf_queue *q, char __user *data, size_t count, loff_t *ppos, int nonblocking) @@ -850,7 +911,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, } /* Copy to userspace */ - retval = CALL(q, video_copy_to_user, q, data, count, nonblocking); + retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking); if (retval < 0) goto done; @@ -862,10 +923,11 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, q->read_buf = NULL; } - done: +done: mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_read_one); /* Locking: Caller holds q->vb_lock */ static int __videobuf_read_start(struct videobuf_queue *q) @@ -917,7 +979,6 @@ static void __videobuf_read_stop(struct videobuf_queue *q) q->bufs[i] = NULL; } q->read_buf = NULL; - } int videobuf_read_start(struct videobuf_queue *q) @@ -930,6 +991,7 @@ int videobuf_read_start(struct videobuf_queue *q) return rc; } +EXPORT_SYMBOL_GPL(videobuf_read_start); void videobuf_read_stop(struct videobuf_queue *q) { @@ -937,6 +999,7 @@ void videobuf_read_stop(struct videobuf_queue *q) __videobuf_read_stop(q); mutex_unlock(&q->vb_lock); } +EXPORT_SYMBOL_GPL(videobuf_read_stop); void videobuf_stop(struct videobuf_queue *q) { @@ -950,7 +1013,7 @@ void videobuf_stop(struct videobuf_queue *q) mutex_unlock(&q->vb_lock); } - +EXPORT_SYMBOL_GPL(videobuf_stop); ssize_t videobuf_read_stream(struct videobuf_queue *q, char __user *data, size_t count, loff_t *ppos, @@ -990,7 +1053,7 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, } if (q->read_buf->state == VIDEOBUF_DONE) { - rc = CALL(q, copy_stream, q, data + retval, count, + rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count, retval, vbihack, nonblocking); if (rc < 0) { retval = rc; @@ -1019,10 +1082,11 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, break; } - done: +done: mutex_unlock(&q->vb_lock); return retval; } +EXPORT_SYMBOL_GPL(videobuf_read_stream); unsigned int videobuf_poll_stream(struct file *file, struct videobuf_queue *q, @@ -1056,27 +1120,51 @@ unsigned int videobuf_poll_stream(struct file *file, if (0 == rc) { poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || - buf->state == VIDEOBUF_ERROR) - rc = POLLIN|POLLRDNORM; + buf->state == VIDEOBUF_ERROR) { + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + rc = POLLOUT | POLLWRNORM; + break; + default: + rc = POLLIN | POLLRDNORM; + break; + } + } } mutex_unlock(&q->vb_lock); return rc; } +EXPORT_SYMBOL_GPL(videobuf_poll_stream); -int videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma) +int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) { - int retval; + int rc = -EINVAL; + int i; MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); + if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) { + dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n"); + return -EINVAL; + } + mutex_lock(&q->vb_lock); - retval = CALL(q, mmap_mapper, q, vma); - q->is_mmapped = 1; + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + struct videobuf_buffer *buf = q->bufs[i]; + + if (buf && buf->memory == V4L2_MEMORY_MMAP && + buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) { + rc = CALL(q, mmap_mapper, q, buf, vma); + break; + } + } mutex_unlock(&q->vb_lock); - return retval; + return rc; } +EXPORT_SYMBOL_GPL(videobuf_mmap_mapper); #ifdef CONFIG_VIDEO_V4L1_COMPAT int videobuf_cgmbuf(struct videobuf_queue *q, @@ -1107,33 +1195,3 @@ int videobuf_cgmbuf(struct videobuf_queue *q, EXPORT_SYMBOL_GPL(videobuf_cgmbuf); #endif -/* --------------------------------------------------------------------- */ - -EXPORT_SYMBOL_GPL(videobuf_waiton); -EXPORT_SYMBOL_GPL(videobuf_iolock); - -EXPORT_SYMBOL_GPL(videobuf_alloc); - -EXPORT_SYMBOL_GPL(videobuf_queue_core_init); -EXPORT_SYMBOL_GPL(videobuf_queue_cancel); -EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); - -EXPORT_SYMBOL_GPL(videobuf_next_field); -EXPORT_SYMBOL_GPL(videobuf_reqbufs); -EXPORT_SYMBOL_GPL(videobuf_querybuf); -EXPORT_SYMBOL_GPL(videobuf_qbuf); -EXPORT_SYMBOL_GPL(videobuf_dqbuf); -EXPORT_SYMBOL_GPL(videobuf_streamon); -EXPORT_SYMBOL_GPL(videobuf_streamoff); - -EXPORT_SYMBOL_GPL(videobuf_read_start); -EXPORT_SYMBOL_GPL(videobuf_read_stop); -EXPORT_SYMBOL_GPL(videobuf_stop); -EXPORT_SYMBOL_GPL(videobuf_read_stream); -EXPORT_SYMBOL_GPL(videobuf_read_one); -EXPORT_SYMBOL_GPL(videobuf_poll_stream); - -EXPORT_SYMBOL_GPL(__videobuf_mmap_setup); -EXPORT_SYMBOL_GPL(videobuf_mmap_setup); -EXPORT_SYMBOL_GPL(videobuf_mmap_free); -EXPORT_SYMBOL_GPL(videobuf_mmap_mapper); diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index dce4f3aa4af1..74730c624cfc 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -55,14 +55,14 @@ static void videobuf_vm_close(struct vm_area_struct *vma) struct videobuf_queue *q = map->q; int i; - dev_dbg(map->q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", + dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); map->count--; if (0 == map->count) { struct videobuf_dma_contig_memory *mem; - dev_dbg(map->q->dev, "munmap %p q=%p\n", map, q); + dev_dbg(q->dev, "munmap %p q=%p\n", map, q); mutex_lock(&q->vb_lock); /* We need first to cancel streams, before unmapping */ @@ -89,7 +89,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) /* vfree is not atomic - can't be called with IRQ's disabled */ - dev_dbg(map->q->dev, "buf[%d] freeing %p\n", + dev_dbg(q->dev, "buf[%d] freeing %p\n", i, mem->vaddr); dma_free_coherent(q->dev, mem->size, @@ -190,7 +190,7 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, return ret; } -static void *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; @@ -204,7 +204,7 @@ static void *__videobuf_alloc(size_t size) return vb; } -static void *__videobuf_to_vmalloc(struct videobuf_buffer *buf) +static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -263,65 +263,34 @@ static int __videobuf_iolock(struct videobuf_queue *q, return 0; } -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - unsigned int i; - - dev_dbg(q->dev, "%s\n", __func__); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (q->bufs[i] && q->bufs[i]->map) - return -EBUSY; - } - - return 0; -} - static int __videobuf_mmap_mapper(struct videobuf_queue *q, + struct videobuf_buffer *buf, struct vm_area_struct *vma) { struct videobuf_dma_contig_memory *mem; struct videobuf_mapping *map; - unsigned int first; int retval; - unsigned long size, offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long size; dev_dbg(q->dev, "%s\n", __func__); - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - /* look for first buffer to map */ - for (first = 0; first < VIDEO_MAX_FRAME; first++) { - if (!q->bufs[first]) - continue; - - if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) - continue; - if (q->bufs[first]->boff == offset) - break; - } - if (VIDEO_MAX_FRAME == first) { - dev_dbg(q->dev, "invalid user space offset [offset=0x%lx]\n", - offset); - return -EINVAL; - } /* create mapping + update buffer list */ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); if (!map) return -ENOMEM; - q->bufs[first]->map = map; + buf->map = map; map->start = vma->vm_start; map->end = vma->vm_end; map->q = q; - q->bufs[first]->baddr = vma->vm_start; + buf->baddr = vma->vm_start; - mem = q->bufs[first]->priv; + mem = buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - mem->size = PAGE_ALIGN(q->bufs[first]->bsize); + mem->size = PAGE_ALIGN(buf->bsize); mem->vaddr = dma_alloc_coherent(q->dev, mem->size, &mem->dma_handle, GFP_KERNEL); if (!mem->vaddr) { @@ -354,8 +323,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", map, q, vma->vm_start, vma->vm_end, - (long int) q->bufs[first]->bsize, - vma->vm_pgoff, first); + (long int)buf->bsize, + vma->vm_pgoff, buf->i); videobuf_vm_open(vma); @@ -366,69 +335,13 @@ error: return -ENOMEM; } -static int __videobuf_copy_to_user(struct videobuf_queue *q, - char __user *data, size_t count, - int nonblocking) -{ - struct videobuf_dma_contig_memory *mem = q->read_buf->priv; - void *vaddr; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - BUG_ON(!mem->vaddr); - - /* copy to userspace */ - if (count > q->read_buf->size - q->read_off) - count = q->read_buf->size - q->read_off; - - vaddr = mem->vaddr; - - if (copy_to_user(data, vaddr + q->read_off, count)) - return -EFAULT; - - return count; -} - -static int __videobuf_copy_stream(struct videobuf_queue *q, - char __user *data, size_t count, size_t pos, - int vbihack, int nonblocking) -{ - unsigned int *fc; - struct videobuf_dma_contig_memory *mem = q->read_buf->priv; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - if (vbihack) { - /* dirty, undocumented hack -- pass the frame counter - * within the last four bytes of each vbi data block. - * We need that one to maintain backward compatibility - * to all vbi decoding software out there ... */ - fc = (unsigned int *)mem->vaddr; - fc += (q->read_buf->size >> 2) - 1; - *fc = q->read_buf->field_count >> 1; - dev_dbg(q->dev, "vbihack: %d\n", *fc); - } - - /* copy stuff using the common method */ - count = __videobuf_copy_to_user(q, data, count, nonblocking); - - if ((count == -EFAULT) && (pos == 0)) - return -EFAULT; - - return count; -} - static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, .alloc = __videobuf_alloc, .iolock = __videobuf_iolock, - .mmap_free = __videobuf_mmap_free, .mmap_mapper = __videobuf_mmap_mapper, - .video_copy_to_user = __videobuf_copy_to_user, - .copy_stream = __videobuf_copy_stream, - .vmalloc = __videobuf_to_vmalloc, + .vaddr = __videobuf_to_vaddr, }; void videobuf_queue_dma_contig_init(struct videobuf_queue *q, diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index fcd045e7a1c1..8359e6badd36 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -37,8 +37,12 @@ #define MAGIC_DMABUF 0x19721112 #define MAGIC_SG_MEM 0x17890714 -#define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ - { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } +#define MAGIC_CHECK(is, should) \ + if (unlikely((is) != (should))) { \ + printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \ + is, should); \ + BUG(); \ + } static int debug; module_param(debug, int, 0644); @@ -47,13 +51,13 @@ MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg) +#define dprintk(level, fmt, arg...) \ + if (debug >= level) \ + printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg) /* --------------------------------------------------------------------- */ -struct scatterlist* -videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) +struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) { struct scatterlist *sglist; struct page *pg; @@ -73,13 +77,14 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) } return sglist; - err: +err: vfree(sglist); return NULL; } +EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); -struct scatterlist* -videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) +struct scatterlist *videobuf_pages_to_sg(struct page **pages, int nr_pages, + int offset) { struct scatterlist *sglist; int i; @@ -104,20 +109,20 @@ videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) } return sglist; - nopage: - dprintk(2,"sgl: oops - no page\n"); +nopage: + dprintk(2, "sgl: oops - no page\n"); vfree(sglist); return NULL; - highmem: - dprintk(2,"sgl: oops - highmem page\n"); +highmem: + dprintk(2, "sgl: oops - highmem page\n"); vfree(sglist); return NULL; } /* --------------------------------------------------------------------- */ -struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf) +struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf) { struct videobuf_dma_sg_memory *mem = buf->priv; BUG_ON(!mem); @@ -126,17 +131,19 @@ struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf) return &mem->dma; } +EXPORT_SYMBOL_GPL(videobuf_to_dma); void videobuf_dma_init(struct videobuf_dmabuf *dma) { - memset(dma,0,sizeof(*dma)); + memset(dma, 0, sizeof(*dma)); dma->magic = MAGIC_DMABUF; } +EXPORT_SYMBOL_GPL(videobuf_dma_init); static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) { - unsigned long first,last; + unsigned long first, last; int err, rw = 0; dma->direction = direction; @@ -155,21 +162,21 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT; dma->offset = data & ~PAGE_MASK; dma->nr_pages = last-first+1; - dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*), - GFP_KERNEL); + dma->pages = kmalloc(dma->nr_pages * sizeof(struct page *), GFP_KERNEL); if (NULL == dma->pages) return -ENOMEM; - dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n", - data,size,dma->nr_pages); - err = get_user_pages(current,current->mm, + dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n", + data, size, dma->nr_pages); + + err = get_user_pages(current, current->mm, data & PAGE_MASK, dma->nr_pages, rw == READ, 1, /* force */ dma->pages, NULL); if (err != dma->nr_pages) { dma->nr_pages = (err >= 0) ? err : 0; - dprintk(1,"get_user_pages: err=%d [%d]\n",err,dma->nr_pages); + dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages); return err < 0 ? err : -EINVAL; } return 0; @@ -179,48 +186,58 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) { int ret; + down_read(¤t->mm->mmap_sem); ret = videobuf_dma_init_user_locked(dma, direction, data, size); up_read(¤t->mm->mmap_sem); return ret; } +EXPORT_SYMBOL_GPL(videobuf_dma_init_user); int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, int nr_pages) { - dprintk(1,"init kernel [%d pages]\n",nr_pages); + dprintk(1, "init kernel [%d pages]\n", nr_pages); + dma->direction = direction; dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT); if (NULL == dma->vmalloc) { - dprintk(1,"vmalloc_32(%d pages) failed\n",nr_pages); + dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); return -ENOMEM; } - dprintk(1,"vmalloc is at addr 0x%08lx, size=%d\n", + + dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", (unsigned long)dma->vmalloc, nr_pages << PAGE_SHIFT); - memset(dma->vmalloc,0,nr_pages << PAGE_SHIFT); + + memset(dma->vmalloc, 0, nr_pages << PAGE_SHIFT); dma->nr_pages = nr_pages; + return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, dma_addr_t addr, int nr_pages) { - dprintk(1,"init overlay [%d pages @ bus 0x%lx]\n", - nr_pages,(unsigned long)addr); + dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n", + nr_pages, (unsigned long)addr); dma->direction = direction; + if (0 == addr) return -EINVAL; dma->bus_addr = addr; dma->nr_pages = nr_pages; + return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); -int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) +int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) { - MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + MAGIC_CHECK(dma->magic, MAGIC_DMABUF); BUG_ON(0 == dma->nr_pages); if (dma->pages) { @@ -228,20 +245,21 @@ int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) dma->offset); } if (dma->vmalloc) { - dma->sglist = videobuf_vmalloc_to_sg - (dma->vmalloc,dma->nr_pages); + dma->sglist = videobuf_vmalloc_to_sg(dma->vmalloc, + dma->nr_pages); } if (dma->bus_addr) { dma->sglist = vmalloc(sizeof(*dma->sglist)); if (NULL != dma->sglist) { - dma->sglen = 1; - sg_dma_address(&dma->sglist[0]) = dma->bus_addr & PAGE_MASK; - dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK; - sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE; + dma->sglen = 1; + sg_dma_address(&dma->sglist[0]) = dma->bus_addr + & PAGE_MASK; + dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK; + sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE; } } if (NULL == dma->sglist) { - dprintk(1,"scatterlist is NULL\n"); + dprintk(1, "scatterlist is NULL\n"); return -ENOMEM; } if (!dma->bus_addr) { @@ -249,47 +267,43 @@ int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) dma->nr_pages, dma->direction); if (0 == dma->sglen) { printk(KERN_WARNING - "%s: videobuf_map_sg failed\n",__func__); + "%s: videobuf_map_sg failed\n", __func__); vfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; return -ENOMEM; } } - return 0; -} - -int videobuf_dma_sync(struct videobuf_queue *q, struct videobuf_dmabuf *dma) -{ - MAGIC_CHECK(dma->magic, MAGIC_DMABUF); - BUG_ON(!dma->sglen); - dma_sync_sg_for_cpu(q->dev, dma->sglist, dma->nr_pages, dma->direction); return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_map); -int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma) +int videobuf_dma_unmap(struct videobuf_queue *q, struct videobuf_dmabuf *dma) { MAGIC_CHECK(dma->magic, MAGIC_DMABUF); + if (!dma->sglen) return 0; - dma_unmap_sg(q->dev, dma->sglist, dma->nr_pages, dma->direction); + dma_unmap_sg(q->dev, dma->sglist, dma->sglen, dma->direction); vfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; + return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_unmap); int videobuf_dma_free(struct videobuf_dmabuf *dma) { - MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + int i; + MAGIC_CHECK(dma->magic, MAGIC_DMABUF); BUG_ON(dma->sglen); if (dma->pages) { - int i; - for (i=0; i < dma->nr_pages; i++) + for (i = 0; i < dma->nr_pages; i++) page_cache_release(dma->pages[i]); kfree(dma->pages); dma->pages = NULL; @@ -298,12 +312,13 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) vfree(dma->vmalloc); dma->vmalloc = NULL; - if (dma->bus_addr) { + if (dma->bus_addr) dma->bus_addr = 0; - } dma->direction = DMA_NONE; + return 0; } +EXPORT_SYMBOL_GPL(videobuf_dma_free); /* --------------------------------------------------------------------- */ @@ -315,6 +330,7 @@ int videobuf_sg_dma_map(struct device *dev, struct videobuf_dmabuf *dma) return videobuf_dma_map(&q, dma); } +EXPORT_SYMBOL_GPL(videobuf_sg_dma_map); int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) { @@ -324,49 +340,48 @@ int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) return videobuf_dma_unmap(&q, dma); } +EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap); /* --------------------------------------------------------------------- */ -static void -videobuf_vm_open(struct vm_area_struct *vma) +static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; - dprintk(2,"vm_open %p [count=%d,vma=%08lx-%08lx]\n",map, - map->count,vma->vm_start,vma->vm_end); + dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map, + map->count, vma->vm_start, vma->vm_end); + map->count++; } -static void -videobuf_vm_close(struct vm_area_struct *vma) +static void videobuf_vm_close(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; struct videobuf_queue *q = map->q; struct videobuf_dma_sg_memory *mem; int i; - dprintk(2,"vm_close %p [count=%d,vma=%08lx-%08lx]\n",map, - map->count,vma->vm_start,vma->vm_end); + dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map, + map->count, vma->vm_start, vma->vm_end); map->count--; if (0 == map->count) { - dprintk(1,"munmap %p q=%p\n",map,q); + dprintk(1, "munmap %p q=%p\n", map, q); mutex_lock(&q->vb_lock); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) continue; - mem=q->bufs[i]->priv; - + mem = q->bufs[i]->priv; if (!mem) continue; - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); if (q->bufs[i]->map != map) continue; q->bufs[i]->map = NULL; q->bufs[i]->baddr = 0; - q->ops->buf_release(q,q->bufs[i]); + q->ops->buf_release(q, q->bufs[i]); } mutex_unlock(&q->vb_lock); kfree(map); @@ -380,26 +395,27 @@ videobuf_vm_close(struct vm_area_struct *vma) * now ...). Bounce buffers don't work very well for the data rates * video capture has. */ -static int -videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct page *page; - dprintk(3,"fault: fault @ %08lx [vma %08lx-%08lx]\n", - (unsigned long)vmf->virtual_address,vma->vm_start,vma->vm_end); + dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n", + (unsigned long)vmf->virtual_address, + vma->vm_start, vma->vm_end); + page = alloc_page(GFP_USER | __GFP_DMA32); if (!page) return VM_FAULT_OOM; clear_user_highpage(page, (unsigned long)vmf->virtual_address); vmf->page = page; + return 0; } -static const struct vm_operations_struct videobuf_vm_ops = -{ - .open = videobuf_vm_open, - .close = videobuf_vm_close, - .fault = videobuf_vm_fault, +static const struct vm_operations_struct videobuf_vm_ops = { + .open = videobuf_vm_open, + .close = videobuf_vm_close, + .fault = videobuf_vm_fault, }; /* --------------------------------------------------------------------- @@ -412,28 +428,28 @@ static const struct vm_operations_struct videobuf_vm_ops = struct videobuf_dma_sg_memory */ -static void *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_dma_sg_memory *mem; struct videobuf_buffer *vb; - vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); + vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); if (!vb) return vb; - mem = vb->priv = ((char *)vb)+size; - mem->magic=MAGIC_SG_MEM; + mem = vb->priv = ((char *)vb) + size; + mem->magic = MAGIC_SG_MEM; videobuf_dma_init(&mem->dma); - dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", - __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), - mem,(long)sizeof(*mem)); + dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n", + __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb), + mem, (long)sizeof(*mem)); return vb; } -static void *__videobuf_to_vmalloc (struct videobuf_buffer *buf) +static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_sg_memory *mem = buf->priv; BUG_ON(!mem); @@ -443,11 +459,11 @@ static void *__videobuf_to_vmalloc (struct videobuf_buffer *buf) return mem->dma.vmalloc; } -static int __videobuf_iolock (struct videobuf_queue* q, - struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf) +static int __videobuf_iolock(struct videobuf_queue *q, + struct videobuf_buffer *vb, + struct v4l2_framebuffer *fbuf) { - int err,pages; + int err, pages; dma_addr_t bus; struct videobuf_dma_sg_memory *mem = vb->priv; BUG_ON(!mem); @@ -460,16 +476,16 @@ static int __videobuf_iolock (struct videobuf_queue* q, if (0 == vb->baddr) { /* no userspace addr -- kernel bounce buffer */ pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; - err = videobuf_dma_init_kernel( &mem->dma, - DMA_FROM_DEVICE, - pages ); + err = videobuf_dma_init_kernel(&mem->dma, + DMA_FROM_DEVICE, + pages); if (0 != err) return err; } else if (vb->memory == V4L2_MEMORY_USERPTR) { /* dma directly to userspace */ - err = videobuf_dma_init_user( &mem->dma, - DMA_FROM_DEVICE, - vb->baddr,vb->bsize ); + err = videobuf_dma_init_user(&mem->dma, + DMA_FROM_DEVICE, + vb->baddr, vb->bsize); if (0 != err) return err; } else { @@ -515,43 +531,27 @@ static int __videobuf_sync(struct videobuf_queue *q, struct videobuf_buffer *buf) { struct videobuf_dma_sg_memory *mem = buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); + BUG_ON(!mem || !mem->dma.sglen); - return videobuf_dma_sync(q,&mem->dma); -} - -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - int i; + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); + MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (q->bufs[i]) { - if (q->bufs[i]->map) - return -EBUSY; - } - } + dma_sync_sg_for_cpu(q->dev, mem->dma.sglist, + mem->dma.sglen, mem->dma.direction); return 0; } static int __videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma) + struct videobuf_buffer *buf, + struct vm_area_struct *vma) { - struct videobuf_dma_sg_memory *mem; + struct videobuf_dma_sg_memory *mem = buf->priv; struct videobuf_mapping *map; - unsigned int first,last,size,i; + unsigned int first, last, size = 0, i; int retval; retval = -EINVAL; - if (!(vma->vm_flags & VM_WRITE)) { - dprintk(1,"mmap app bug: PROT_WRITE please\n"); - goto done; - } - if (!(vma->vm_flags & VM_SHARED)) { - dprintk(1,"mmap app bug: MAP_SHARED please\n"); - goto done; - } /* This function maintains backwards compatibility with V4L1 and will * map more than one buffer if the vma length is equal to the combined @@ -561,48 +561,52 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, * TODO: Allow drivers to specify if they support this mode */ + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); + /* look for first buffer to map */ for (first = 0; first < VIDEO_MAX_FRAME; first++) { - if (NULL == q->bufs[first]) - continue; - mem=q->bufs[first]->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); - - if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) - continue; - if (q->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT)) + if (buf == q->bufs[first]) { + size = PAGE_ALIGN(q->bufs[first]->bsize); break; + } } - if (VIDEO_MAX_FRAME == first) { - dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n", - (vma->vm_pgoff << PAGE_SHIFT)); + + /* paranoia, should never happen since buf is always valid. */ + if (!size) { + dprintk(1, "mmap app bug: offset invalid [offset=0x%lx]\n", + (vma->vm_pgoff << PAGE_SHIFT)); goto done; } - /* look for last buffer to map */ - for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) { - if (NULL == q->bufs[last]) - continue; - if (V4L2_MEMORY_MMAP != q->bufs[last]->memory) - continue; - if (q->bufs[last]->map) { - retval = -EBUSY; + last = first; +#ifdef CONFIG_VIDEO_V4L1_COMPAT + if (size != (vma->vm_end - vma->vm_start)) { + /* look for last buffer to map */ + for (last = first + 1; last < VIDEO_MAX_FRAME; last++) { + if (NULL == q->bufs[last]) + continue; + if (V4L2_MEMORY_MMAP != q->bufs[last]->memory) + continue; + if (q->bufs[last]->map) { + retval = -EBUSY; + goto done; + } + size += PAGE_ALIGN(q->bufs[last]->bsize); + if (size == (vma->vm_end - vma->vm_start)) + break; + } + if (VIDEO_MAX_FRAME == last) { + dprintk(1, "mmap app bug: size invalid [size=0x%lx]\n", + (vma->vm_end - vma->vm_start)); goto done; } - size += PAGE_ALIGN(q->bufs[last]->bsize); - if (size == (vma->vm_end - vma->vm_start)) - break; - } - if (VIDEO_MAX_FRAME == last) { - dprintk(1,"mmap app bug: size invalid [size=0x%lx]\n", - (vma->vm_end - vma->vm_start)); - goto done; } +#endif /* create mapping + update buffer list */ retval = -ENOMEM; - map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL); + map = kmalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); if (NULL == map) goto done; @@ -623,72 +627,22 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ vma->vm_private_data = map; - dprintk(1,"mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n", - map,q,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last); + dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n", + map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last); retval = 0; - done: +done: return retval; } -static int __videobuf_copy_to_user ( struct videobuf_queue *q, - char __user *data, size_t count, - int nonblocking ) -{ - struct videobuf_dma_sg_memory *mem = q->read_buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); - - /* copy to userspace */ - if (count > q->read_buf->size - q->read_off) - count = q->read_buf->size - q->read_off; - - if (copy_to_user(data, mem->dma.vmalloc+q->read_off, count)) - return -EFAULT; - - return count; -} - -static int __videobuf_copy_stream ( struct videobuf_queue *q, - char __user *data, size_t count, size_t pos, - int vbihack, int nonblocking ) -{ - unsigned int *fc; - struct videobuf_dma_sg_memory *mem = q->read_buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); - - if (vbihack) { - /* dirty, undocumented hack -- pass the frame counter - * within the last four bytes of each vbi data block. - * We need that one to maintain backward compatibility - * to all vbi decoding software out there ... */ - fc = (unsigned int*)mem->dma.vmalloc; - fc += (q->read_buf->size>>2) -1; - *fc = q->read_buf->field_count >> 1; - dprintk(1,"vbihack: %d\n",*fc); - } - - /* copy stuff using the common method */ - count = __videobuf_copy_to_user (q,data,count,nonblocking); - - if ( (count==-EFAULT) && (0 == pos) ) - return -EFAULT; - - return count; -} - static struct videobuf_qtype_ops sg_ops = { .magic = MAGIC_QTYPE_OPS, .alloc = __videobuf_alloc, .iolock = __videobuf_iolock, .sync = __videobuf_sync, - .mmap_free = __videobuf_mmap_free, .mmap_mapper = __videobuf_mmap_mapper, - .video_copy_to_user = __videobuf_copy_to_user, - .copy_stream = __videobuf_copy_stream, - .vmalloc = __videobuf_to_vmalloc, + .vaddr = __videobuf_to_vaddr, }; void *videobuf_sg_alloc(size_t size) @@ -702,8 +656,9 @@ void *videobuf_sg_alloc(size_t size) return videobuf_alloc(&q); } +EXPORT_SYMBOL_GPL(videobuf_sg_alloc); -void videobuf_queue_sg_init(struct videobuf_queue* q, +void videobuf_queue_sg_init(struct videobuf_queue *q, const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, @@ -715,29 +670,5 @@ void videobuf_queue_sg_init(struct videobuf_queue* q, videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, priv, &sg_ops); } - -/* --------------------------------------------------------------------- */ - -EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); - -EXPORT_SYMBOL_GPL(videobuf_to_dma); -EXPORT_SYMBOL_GPL(videobuf_dma_init); -EXPORT_SYMBOL_GPL(videobuf_dma_init_user); -EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); -EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); -EXPORT_SYMBOL_GPL(videobuf_dma_map); -EXPORT_SYMBOL_GPL(videobuf_dma_sync); -EXPORT_SYMBOL_GPL(videobuf_dma_unmap); -EXPORT_SYMBOL_GPL(videobuf_dma_free); - -EXPORT_SYMBOL_GPL(videobuf_sg_dma_map); -EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap); -EXPORT_SYMBOL_GPL(videobuf_sg_alloc); - EXPORT_SYMBOL_GPL(videobuf_queue_sg_init); -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 0afb62e63d99..3f76398968b8 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -67,7 +67,7 @@ static int videobuf_dvb_thread(void *data) try_to_freeze(); /* feed buffer data to demux */ - outp = videobuf_queue_to_vmalloc (&dvb->dvbq, buf); + outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf); if (buf->state == VIDEOBUF_DONE) dvb_dmx_swfilter(&dvb->demux, outp, diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 136e09383c06..583728f4c221 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -30,8 +30,12 @@ #define MAGIC_DMABUF 0x17760309 #define MAGIC_VMAL_MEM 0x18221223 -#define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ - { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } +#define MAGIC_CHECK(is, should) \ + if (unlikely((is) != (should))) { \ + printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \ + is, should); \ + BUG(); \ + } static int debug; module_param(debug, int, 0644); @@ -40,19 +44,19 @@ MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg) +#define dprintk(level, fmt, arg...) \ + if (debug >= level) \ + printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg) /***************************************************************************/ -static void -videobuf_vm_open(struct vm_area_struct *vma) +static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; - dprintk(2,"vm_open %p [count=%u,vma=%08lx-%08lx]\n",map, - map->count,vma->vm_start,vma->vm_end); + dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map, + map->count, vma->vm_start, vma->vm_end); map->count++; } @@ -63,7 +67,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) struct videobuf_queue *q = map->q; int i; - dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, + dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, map->count, vma->vm_start, vma->vm_end); map->count--; @@ -116,8 +120,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) return; } -static const struct vm_operations_struct videobuf_vm_ops = -{ +static const struct vm_operations_struct videobuf_vm_ops = { .open = videobuf_vm_open, .close = videobuf_vm_close, }; @@ -132,28 +135,28 @@ static const struct vm_operations_struct videobuf_vm_ops = struct videobuf_dma_sg_memory */ -static void *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_vmalloc_memory *mem; struct videobuf_buffer *vb; - vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); + vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); if (!vb) return vb; - mem = vb->priv = ((char *)vb)+size; - mem->magic=MAGIC_VMAL_MEM; + mem = vb->priv = ((char *)vb) + size; + mem->magic = MAGIC_VMAL_MEM; - dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", - __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), - mem,(long)sizeof(*mem)); + dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n", + __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb), + mem, (long)sizeof(*mem)); return vb; } -static int __videobuf_iolock (struct videobuf_queue* q, - struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf) +static int __videobuf_iolock(struct videobuf_queue *q, + struct videobuf_buffer *vb, + struct v4l2_framebuffer *fbuf) { struct videobuf_vmalloc_memory *mem = vb->priv; int pages; @@ -177,15 +180,13 @@ static int __videobuf_iolock (struct videobuf_queue* q, dprintk(1, "%s memory method USERPTR\n", __func__); -#if 1 if (vb->baddr) { printk(KERN_ERR "USERPTR is currently not supported\n"); return -EINVAL; } -#endif /* The only USERPTR currently supported is the one needed for - read() method. + * read() method. */ mem->vmalloc = vmalloc_user(pages); @@ -210,7 +211,7 @@ static int __videobuf_iolock (struct videobuf_queue* q, /* Try to remap memory */ rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); if (rc < 0) { - printk(KERN_ERR "mmap: remap failed with error %d. ", rc); + printk(KERN_ERR "mmap: remap failed with error %d", rc); return -ENOMEM; } #endif @@ -228,69 +229,29 @@ static int __videobuf_iolock (struct videobuf_queue* q, return 0; } -static int __videobuf_sync(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - return 0; -} - -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - unsigned int i; - - dprintk(1, "%s\n", __func__); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (q->bufs[i]) { - if (q->bufs[i]->map) - return -EBUSY; - } - } - - return 0; -} - static int __videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma) + struct videobuf_buffer *buf, + struct vm_area_struct *vma) { struct videobuf_vmalloc_memory *mem; struct videobuf_mapping *map; - unsigned int first; int retval, pages; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; dprintk(1, "%s\n", __func__); - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - /* look for first buffer to map */ - for (first = 0; first < VIDEO_MAX_FRAME; first++) { - if (NULL == q->bufs[first]) - continue; - - if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) - continue; - if (q->bufs[first]->boff == offset) - break; - } - if (VIDEO_MAX_FRAME == first) { - dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n", - (vma->vm_pgoff << PAGE_SHIFT)); - return -EINVAL; - } /* create mapping + update buffer list */ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); if (NULL == map) return -ENOMEM; - q->bufs[first]->map = map; + buf->map = map; map->start = vma->vm_start; map->end = vma->vm_end; map->q = q; - q->bufs[first]->baddr = vma->vm_start; + buf->baddr = vma->vm_start; - mem = q->bufs[first]->priv; + mem = buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); @@ -300,8 +261,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); goto error; } - dprintk(1, "vmalloc is at addr %p (%d pages)\n", - mem->vmalloc, pages); + dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vmalloc, pages); /* Try to remap memory */ retval = remap_vmalloc_range(vma, mem->vmalloc, 0); @@ -315,10 +275,10 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; vma->vm_private_data = map; - dprintk(1,"mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", + dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", map, q, vma->vm_start, vma->vm_end, - (long int) q->bufs[first]->bsize, - vma->vm_pgoff, first); + (long int)buf->bsize, + vma->vm_pgoff, buf->i); videobuf_vm_open(vma); @@ -330,69 +290,16 @@ error: return -ENOMEM; } -static int __videobuf_copy_to_user ( struct videobuf_queue *q, - char __user *data, size_t count, - int nonblocking ) -{ - struct videobuf_vmalloc_memory *mem=q->read_buf->priv; - BUG_ON (!mem); - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); - - BUG_ON (!mem->vmalloc); - - /* copy to userspace */ - if (count > q->read_buf->size - q->read_off) - count = q->read_buf->size - q->read_off; - - if (copy_to_user(data, mem->vmalloc+q->read_off, count)) - return -EFAULT; - - return count; -} - -static int __videobuf_copy_stream ( struct videobuf_queue *q, - char __user *data, size_t count, size_t pos, - int vbihack, int nonblocking ) -{ - unsigned int *fc; - struct videobuf_vmalloc_memory *mem=q->read_buf->priv; - BUG_ON (!mem); - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); - - if (vbihack) { - /* dirty, undocumented hack -- pass the frame counter - * within the last four bytes of each vbi data block. - * We need that one to maintain backward compatibility - * to all vbi decoding software out there ... */ - fc = (unsigned int*)mem->vmalloc; - fc += (q->read_buf->size>>2) -1; - *fc = q->read_buf->field_count >> 1; - dprintk(1,"vbihack: %d\n",*fc); - } - - /* copy stuff using the common method */ - count = __videobuf_copy_to_user (q,data,count,nonblocking); - - if ( (count==-EFAULT) && (0 == pos) ) - return -EFAULT; - - return count; -} - static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, .alloc = __videobuf_alloc, .iolock = __videobuf_iolock, - .sync = __videobuf_sync, - .mmap_free = __videobuf_mmap_free, .mmap_mapper = __videobuf_mmap_mapper, - .video_copy_to_user = __videobuf_copy_to_user, - .copy_stream = __videobuf_copy_stream, - .vmalloc = videobuf_to_vmalloc, + .vaddr = videobuf_to_vmalloc, }; -void videobuf_queue_vmalloc_init(struct videobuf_queue* q, +void videobuf_queue_vmalloc_init(struct videobuf_queue *q, const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, @@ -404,20 +311,19 @@ void videobuf_queue_vmalloc_init(struct videobuf_queue* q, videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, priv, &qops); } - EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init); -void *videobuf_to_vmalloc (struct videobuf_buffer *buf) +void *videobuf_to_vmalloc(struct videobuf_buffer *buf) { - struct videobuf_vmalloc_memory *mem=buf->priv; - BUG_ON (!mem); - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); + struct videobuf_vmalloc_memory *mem = buf->priv; + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); return mem->vmalloc; } EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); -void videobuf_vmalloc_free (struct videobuf_buffer *buf) +void videobuf_vmalloc_free(struct videobuf_buffer *buf) { struct videobuf_vmalloc_memory *mem = buf->priv; @@ -442,8 +348,3 @@ void videobuf_vmalloc_free (struct videobuf_buffer *buf) } EXPORT_SYMBOL_GPL(videobuf_vmalloc_free); -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index cdbe70385c12..e17b6fee046b 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -13,29 +13,23 @@ * License, or (at your option) any later version */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/errno.h> -#include <linux/fs.h> #include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/ioport.h> #include <linux/init.h> #include <linux/sched.h> -#include <linux/pci.h> -#include <linux/random.h> +#include <linux/slab.h> +#include <linux/font.h> #include <linux/version.h> #include <linux/mutex.h> #include <linux/videodev2.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> #include <linux/kthread.h> -#include <linux/highmem.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) #include <linux/freezer.h> +#endif #include <media/videobuf-vmalloc.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include "font.h" +#include <media/v4l2-common.h> #define VIVI_MODULE_NAME "vivi" @@ -44,8 +38,11 @@ #define WAKE_DENOMINATOR 1001 #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ +#define MAX_WIDTH 1920 +#define MAX_HEIGHT 1200 + #define VIVI_MAJOR_VERSION 0 -#define VIVI_MINOR_VERSION 6 +#define VIVI_MINOR_VERSION 7 #define VIVI_RELEASE 0 #define VIVI_VERSION \ KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE) @@ -70,56 +67,8 @@ static unsigned int vid_limit = 16; module_param(vid_limit, uint, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); - -/* supported controls */ -static struct v4l2_queryctrl vivi_qctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 65535, - .flags = V4L2_CTRL_FLAG_SLIDER, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 127, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 0x10, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 127, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_SLIDER, - } -}; +/* Global font descriptor */ +static const u8 *font8x16; #define dprintk(dev, level, fmt, arg...) \ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) @@ -214,41 +163,38 @@ struct vivi_dev { struct list_head vivi_devlist; struct v4l2_device v4l2_dev; + /* controls */ + int brightness; + int contrast; + int saturation; + int hue; + int volume; + spinlock_t slock; struct mutex mutex; - int users; - /* various device info */ struct video_device *vfd; struct vivi_dmaqueue vidq; /* Several counters */ - int h, m, s, ms; + unsigned ms; unsigned long jiffies; - char timestr[13]; int mv_count; /* Controls bars movement */ /* Input Number */ int input; - /* Control 'registers' */ - int qctl_regs[ARRAY_SIZE(vivi_qctrl)]; -}; - -struct vivi_fh { - struct vivi_dev *dev; - /* video capture */ struct vivi_fmt *fmt; unsigned int width, height; struct videobuf_queue vb_vidq; - enum v4l2_buf_type type; - unsigned char bars[8][3]; - int input; /* Input Number on bars */ + unsigned long generating; + u8 bars[9][3]; + u8 line[MAX_WIDTH * 4]; }; /* ------------------------------------------------------------------ @@ -259,19 +205,20 @@ struct vivi_fh { enum colors { WHITE, - AMBAR, + AMBER, CYAN, GREEN, MAGENTA, RED, BLUE, BLACK, + TEXT_BLACK, }; - /* R G B */ +/* R G B */ #define COLOR_WHITE {204, 204, 204} -#define COLOR_AMBAR {208, 208, 0} -#define COLOR_CIAN { 0, 206, 206} +#define COLOR_AMBER {208, 208, 0} +#define COLOR_CYAN { 0, 206, 206} #define COLOR_GREEN { 0, 239, 0} #define COLOR_MAGENTA {239, 0, 239} #define COLOR_RED {205, 0, 0} @@ -279,56 +226,24 @@ enum colors { #define COLOR_BLACK { 0, 0, 0} struct bar_std { - u8 bar[8][3]; + u8 bar[9][3]; }; /* Maximum number of bars are 10 - otherwise, the input print code should be modified */ static struct bar_std bars[] = { { /* Standard ITU-R color bar sequence */ - { - COLOR_WHITE, - COLOR_AMBAR, - COLOR_CIAN, - COLOR_GREEN, - COLOR_MAGENTA, - COLOR_RED, - COLOR_BLUE, - COLOR_BLACK, - } + { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN, + COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK } }, { - { - COLOR_WHITE, - COLOR_AMBAR, - COLOR_BLACK, - COLOR_WHITE, - COLOR_AMBAR, - COLOR_BLACK, - COLOR_WHITE, - COLOR_AMBAR, - } + { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, + COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK } }, { - { - COLOR_WHITE, - COLOR_CIAN, - COLOR_BLACK, - COLOR_WHITE, - COLOR_CIAN, - COLOR_BLACK, - COLOR_WHITE, - COLOR_CIAN, - } + { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, + COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK } }, { - { - COLOR_WHITE, - COLOR_GREEN, - COLOR_BLACK, - COLOR_WHITE, - COLOR_GREEN, - COLOR_BLACK, - COLOR_WHITE, - COLOR_GREEN, - } + { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, + COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK } }, }; @@ -344,21 +259,18 @@ static struct bar_std bars[] = { (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128) /* precalculate color bar values to speed up rendering */ -static void precalculate_bars(struct vivi_fh *fh) +static void precalculate_bars(struct vivi_dev *dev) { - struct vivi_dev *dev = fh->dev; - unsigned char r, g, b; + u8 r, g, b; int k, is_yuv; - fh->input = dev->input; - - for (k = 0; k < 8; k++) { - r = bars[fh->input].bar[k][0]; - g = bars[fh->input].bar[k][1]; - b = bars[fh->input].bar[k][2]; + for (k = 0; k < 9; k++) { + r = bars[dev->input].bar[k][0]; + g = bars[dev->input].bar[k][1]; + b = bars[dev->input].bar[k][2]; is_yuv = 0; - switch (fh->fmt->fourcc) { + switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: is_yuv = 1; @@ -378,16 +290,15 @@ static void precalculate_bars(struct vivi_fh *fh) } if (is_yuv) { - fh->bars[k][0] = TO_Y(r, g, b); /* Luma */ - fh->bars[k][1] = TO_U(r, g, b); /* Cb */ - fh->bars[k][2] = TO_V(r, g, b); /* Cr */ + dev->bars[k][0] = TO_Y(r, g, b); /* Luma */ + dev->bars[k][1] = TO_U(r, g, b); /* Cb */ + dev->bars[k][2] = TO_V(r, g, b); /* Cr */ } else { - fh->bars[k][0] = r; - fh->bars[k][1] = g; - fh->bars[k][2] = b; + dev->bars[k][0] = r; + dev->bars[k][1] = g; + dev->bars[k][2] = b; } } - } #define TSTAMP_MIN_Y 24 @@ -395,20 +306,20 @@ static void precalculate_bars(struct vivi_fh *fh) #define TSTAMP_INPUT_X 10 #define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X) -static void gen_twopix(struct vivi_fh *fh, unsigned char *buf, int colorpos) +static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) { - unsigned char r_y, g_u, b_v; - unsigned char *p; + u8 r_y, g_u, b_v; int color; + u8 *p; - r_y = fh->bars[colorpos][0]; /* R or precalculated Y */ - g_u = fh->bars[colorpos][1]; /* G or precalculated U */ - b_v = fh->bars[colorpos][2]; /* B or precalculated V */ + r_y = dev->bars[colorpos][0]; /* R or precalculated Y */ + g_u = dev->bars[colorpos][1]; /* G or precalculated U */ + b_v = dev->bars[colorpos][2]; /* B or precalculated V */ for (color = 0; color < 4; color++) { p = buf + color; - switch (fh->fmt->fourcc) { + switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: switch (color) { case 0: @@ -489,123 +400,88 @@ static void gen_twopix(struct vivi_fh *fh, unsigned char *buf, int colorpos) } } -static void gen_line(struct vivi_fh *fh, char *basep, int inipos, int wmax, - int hmax, int line, int count, char *timestr) +static void precalculate_line(struct vivi_dev *dev) { - int w, i, j; - int pos = inipos; - char *s; - u8 chr; - - /* We will just duplicate the second pixel at the packet */ - wmax /= 2; + int w; - /* Generate a standard color bar pattern */ - for (w = 0; w < wmax; w++) { - int colorpos = ((w + count) * 8/(wmax + 1)) % 8; + for (w = 0; w < dev->width * 2; w += 2) { + int colorpos = (w / (dev->width / 8) % 8); - gen_twopix(fh, basep + pos, colorpos); - pos += 4; /* only 16 bpp supported for now */ + gen_twopix(dev, dev->line + w * 2, colorpos); } +} - /* Prints input entry number */ - - /* Checks if it is possible to input number */ - if (TSTAMP_MAX_Y >= hmax) - goto end; - - if (TSTAMP_INPUT_X + strlen(timestr) >= wmax) - goto end; - - if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) { - chr = rom8x16_bits[fh->input * 16 + line - TSTAMP_MIN_Y]; - pos = TSTAMP_INPUT_X; - for (i = 0; i < 7; i++) { - /* Draw white font on black background */ - if (chr & 1 << (7 - i)) - gen_twopix(fh, basep + pos, WHITE); - else - gen_twopix(fh, basep + pos, BLACK); - pos += 2; - } - } +static void gen_text(struct vivi_dev *dev, char *basep, + int y, int x, char *text) +{ + int line; - /* Checks if it is possible to show timestamp */ - if (TSTAMP_MIN_X + strlen(timestr) >= wmax) - goto end; + /* Checks if it is possible to show string */ + if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width) + return; /* Print stream time */ - if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) { - j = TSTAMP_MIN_X; - for (s = timestr; *s; s++) { - chr = rom8x16_bits[(*s-0x30)*16+line-TSTAMP_MIN_Y]; - for (i = 0; i < 7; i++) { - pos = inipos + j * 2; + for (line = y; line < y + 16; line++) { + int j = 0; + char *pos = basep + line * dev->width * 2 + x * 2; + char *s; + + for (s = text; *s; s++) { + u8 chr = font8x16[*s * 16 + line - y]; + int i; + + for (i = 0; i < 7; i++, j++) { /* Draw white font on black background */ - if (chr & 1 << (7 - i)) - gen_twopix(fh, basep + pos, WHITE); + if (chr & (1 << (7 - i))) + gen_twopix(dev, pos + j * 2, WHITE); else - gen_twopix(fh, basep + pos, BLACK); - j++; + gen_twopix(dev, pos + j * 2, TEXT_BLACK); } } } - -end: - return; } -static void vivi_fillbuff(struct vivi_fh *fh, struct vivi_buffer *buf) +static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { - struct vivi_dev *dev = fh->dev; - int h , pos = 0; - int hmax = buf->vb.height; - int wmax = buf->vb.width; + int hmax = buf->vb.height; + int wmax = buf->vb.width; struct timeval ts; - char *tmpbuf; void *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned ms; + char str[100]; + int h, line = 1; if (!vbuf) return; - tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); - if (!tmpbuf) - return; - - for (h = 0; h < hmax; h++) { - gen_line(fh, tmpbuf, 0, wmax, hmax, h, dev->mv_count, - dev->timestr); - memcpy(vbuf + pos, tmpbuf, wmax * 2); - pos += wmax*2; - } - - dev->mv_count++; - - kfree(tmpbuf); + for (h = 0; h < hmax; h++) + memcpy(vbuf + h * wmax * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2); /* Updates stream time */ - dev->ms += jiffies_to_msecs(jiffies-dev->jiffies); + dev->ms += jiffies_to_msecs(jiffies - dev->jiffies); dev->jiffies = jiffies; - if (dev->ms >= 1000) { - dev->ms -= 1000; - dev->s++; - if (dev->s >= 60) { - dev->s -= 60; - dev->m++; - if (dev->m > 60) { - dev->m -= 60; - dev->h++; - if (dev->h > 24) - dev->h -= 24; - } - } - } - sprintf(dev->timestr, "%02d:%02d:%02d:%03d", - dev->h, dev->m, dev->s, dev->ms); - - dprintk(dev, 2, "vivifill at %s: Buffer 0x%08lx size= %d\n", - dev->timestr, (unsigned long)tmpbuf, pos); + ms = dev->ms; + snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ", + (ms / (60 * 60 * 1000)) % 24, + (ms / (60 * 1000)) % 60, + (ms / 1000) % 60, + ms % 1000); + gen_text(dev, vbuf, line++ * 16, 16, str); + snprintf(str, sizeof(str), " %dx%d, input %d ", + dev->width, dev->height, dev->input); + gen_text(dev, vbuf, line++ * 16, 16, str); + + snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ", + dev->brightness, + dev->contrast, + dev->saturation, + dev->hue); + gen_text(dev, vbuf, line++ * 16, 16, str); + snprintf(str, sizeof(str), " volume %3d ", dev->volume); + gen_text(dev, vbuf, line++ * 16, 16, str); + + dev->mv_count += 2; /* Advice that buffer was filled */ buf->vb.field_count++; @@ -614,12 +490,10 @@ static void vivi_fillbuff(struct vivi_fh *fh, struct vivi_buffer *buf) buf->vb.state = VIDEOBUF_DONE; } -static void vivi_thread_tick(struct vivi_fh *fh) +static void vivi_thread_tick(struct vivi_dev *dev) { - struct vivi_buffer *buf; - struct vivi_dev *dev = fh->dev; struct vivi_dmaqueue *dma_q = &dev->vidq; - + struct vivi_buffer *buf; unsigned long flags = 0; dprintk(dev, 1, "Thread tick\n"); @@ -642,22 +516,20 @@ static void vivi_thread_tick(struct vivi_fh *fh) do_gettimeofday(&buf->vb.ts); /* Fill buffer */ - vivi_fillbuff(fh, buf); + vivi_fillbuff(dev, buf); dprintk(dev, 1, "filled buffer %p\n", buf); wake_up(&buf->vb.done); dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i); unlock: spin_unlock_irqrestore(&dev->slock, flags); - return; } #define frames_to_ms(frames) \ ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) -static void vivi_sleep(struct vivi_fh *fh) +static void vivi_sleep(struct vivi_dev *dev) { - struct vivi_dev *dev = fh->dev; struct vivi_dmaqueue *dma_q = &dev->vidq; int timeout; DECLARE_WAITQUEUE(wait, current); @@ -672,7 +544,7 @@ static void vivi_sleep(struct vivi_fh *fh) /* Calculate time to wake up */ timeout = msecs_to_jiffies(frames_to_ms(1)); - vivi_thread_tick(fh); + vivi_thread_tick(dev); schedule_timeout_interruptible(timeout); @@ -683,15 +555,14 @@ stop_task: static int vivi_thread(void *data) { - struct vivi_fh *fh = data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = data; dprintk(dev, 1, "thread started\n"); set_freezable(); for (;;) { - vivi_sleep(fh); + vivi_sleep(dev); if (kthread_should_stop()) break; @@ -700,39 +571,61 @@ static int vivi_thread(void *data) return 0; } -static int vivi_start_thread(struct vivi_fh *fh) +static void vivi_start_generating(struct file *file) { - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); struct vivi_dmaqueue *dma_q = &dev->vidq; - dma_q->frame = 0; - dma_q->ini_jiffies = jiffies; - dprintk(dev, 1, "%s\n", __func__); - dma_q->kthread = kthread_run(vivi_thread, fh, "vivi"); + if (test_and_set_bit(0, &dev->generating)) + return; + file->private_data = dev; + + /* Resets frame counters */ + dev->ms = 0; + dev->mv_count = 0; + dev->jiffies = jiffies; + + dma_q->frame = 0; + dma_q->ini_jiffies = jiffies; + dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name); if (IS_ERR(dma_q->kthread)) { v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); - return PTR_ERR(dma_q->kthread); + clear_bit(0, &dev->generating); + return; } /* Wakes thread */ wake_up_interruptible(&dma_q->wq); dprintk(dev, 1, "returning from %s\n", __func__); - return 0; } -static void vivi_stop_thread(struct vivi_dmaqueue *dma_q) +static void vivi_stop_generating(struct file *file) { - struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq); + struct vivi_dev *dev = video_drvdata(file); + struct vivi_dmaqueue *dma_q = &dev->vidq; dprintk(dev, 1, "%s\n", __func__); + + if (!file->private_data) + return; + if (!test_and_clear_bit(0, &dev->generating)) + return; + /* shutdown control thread */ if (dma_q->kthread) { kthread_stop(dma_q->kthread); dma_q->kthread = NULL; } + videobuf_stop(&dev->vb_vidq); + videobuf_mmap_free(&dev->vb_vidq); +} + +static int vivi_is_generating(struct vivi_dev *dev) +{ + return test_bit(0, &dev->generating); } /* ------------------------------------------------------------------ @@ -741,10 +634,9 @@ static void vivi_stop_thread(struct vivi_dmaqueue *dma_q) static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = vq->priv_data; - *size = fh->width*fh->height*2; + *size = dev->width * dev->height * 2; if (0 == *count) *count = 32; @@ -760,49 +652,43 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) { - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = vq->priv_data; dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state); - if (in_interrupt()) - BUG(); - videobuf_vmalloc_free(&buf->vb); dprintk(dev, 1, "free_buffer: freed\n"); buf->vb.state = VIDEOBUF_NEEDS_INIT; } -#define norm_maxw() 1024 -#define norm_maxh() 768 static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = vq->priv_data; struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); int rc; dprintk(dev, 1, "%s, field=%d\n", __func__, field); - BUG_ON(NULL == fh->fmt); + BUG_ON(NULL == dev->fmt); - if (fh->width < 48 || fh->width > norm_maxw() || - fh->height < 32 || fh->height > norm_maxh()) + if (dev->width < 48 || dev->width > MAX_WIDTH || + dev->height < 32 || dev->height > MAX_HEIGHT) return -EINVAL; - buf->vb.size = fh->width*fh->height*2; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + buf->vb.size = dev->width * dev->height * 2; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; /* These properties only change when queue is idle, see s_fmt */ - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; + buf->fmt = dev->fmt; + buf->vb.width = dev->width; + buf->vb.height = dev->height; buf->vb.field = field; - precalculate_bars(fh); + precalculate_bars(dev); + precalculate_line(dev); if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { rc = videobuf_iolock(vq, &buf->vb, NULL); @@ -811,7 +697,6 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, } buf->vb.state = VIDEOBUF_PREPARED; - return 0; fail: @@ -822,9 +707,8 @@ fail: static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { - struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = vq->priv_data; + struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); struct vivi_dmaqueue *vidq = &dev->vidq; dprintk(dev, 1, "%s\n", __func__); @@ -836,9 +720,8 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) { - struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); - struct vivi_fh *fh = vq->priv_data; - struct vivi_dev *dev = (struct vivi_dev *)fh->dev; + struct vivi_dev *dev = vq->priv_data; + struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); dprintk(dev, 1, "%s\n", __func__); @@ -858,16 +741,14 @@ static struct videobuf_queue_ops vivi_video_qops = { static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); strcpy(cap->driver, "vivi"); strcpy(cap->card, "vivi"); strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); cap->version = VIVI_VERSION; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \ + V4L2_CAP_READWRITE; return 0; } @@ -889,28 +770,25 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->vb_vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = dev->vb_vidq.field; + f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; + (f->fmt.pix.width * dev->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - - return (0); + return 0; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); struct vivi_fmt *fmt; enum v4l2_field field; - unsigned int maxw, maxh; fmt = get_format(f); if (!fmt) { @@ -928,113 +806,109 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - maxw = norm_maxw(); - maxh = norm_maxh(); - f->fmt.pix.field = field; - v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, - &f->fmt.pix.height, 32, maxh, 0, 0); + v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2, + &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; } -/*FIXME: This seems to be generic enough to be at videodev2 */ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct vivi_fh *fh = priv; - struct videobuf_queue *q = &fh->vb_vidq; + struct vivi_dev *dev = video_drvdata(file); + struct videobuf_queue *q = &dev->vb_vidq; - int ret = vidioc_try_fmt_vid_cap(file, fh, f); + int ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret < 0) return ret; mutex_lock(&q->vb_lock); - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - dprintk(fh->dev, 1, "%s queue busy\n", __func__); + if (vivi_is_generating(dev)) { + dprintk(dev, 1, "%s device busy\n", __func__); ret = -EBUSY; goto out; } - fh->fmt = get_format(f); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vb_vidq.field = f->fmt.pix.field; - fh->type = f->type; - + dev->fmt = get_format(f); + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->vb_vidq.field = f->fmt.pix.field; ret = 0; out: mutex_unlock(&q->vb_lock); - return ret; } static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return (videobuf_reqbufs(&fh->vb_vidq, p)); + return videobuf_reqbufs(&dev->vb_vidq, p); } static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return (videobuf_querybuf(&fh->vb_vidq, p)); + return videobuf_querybuf(&dev->vb_vidq, p); } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return (videobuf_qbuf(&fh->vb_vidq, p)); + return videobuf_qbuf(&dev->vb_vidq, p); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return (videobuf_dqbuf(&fh->vb_vidq, p, - file->f_flags & O_NONBLOCK)); + return videobuf_dqbuf(&dev->vb_vidq, p, + file->f_flags & O_NONBLOCK); } #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); - return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + return videobuf_cgmbuf(&dev->vb_vidq, mbuf, 8); } #endif static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); + int ret; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + ret = videobuf_streamon(&dev->vb_vidq); + if (ret) + return ret; - return videobuf_streamon(&fh->vb_vidq); + vivi_start_generating(file); + return 0; } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { - struct vivi_fh *fh = priv; + struct vivi_dev *dev = video_drvdata(file); + int ret; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - - return videobuf_streamoff(&fh->vb_vidq); + ret = videobuf_streamoff(&dev->vb_vidq); + if (!ret) + vivi_stop_generating(file); + return ret; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) @@ -1052,80 +926,104 @@ static int vidioc_enum_input(struct file *file, void *priv, inp->type = V4L2_INPUT_TYPE_CAMERA; inp->std = V4L2_STD_525_60; sprintf(inp->name, "Camera %u", inp->index); - - return (0); + return 0; } static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); *i = dev->input; - - return (0); + return 0; } + static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); if (i >= NUM_INPUTS) return -EINVAL; dev->input = i; - precalculate_bars(fh); - - return (0); + precalculate_bars(dev); + precalculate_line(dev); + return 0; } - /* --- controls ---------------------------------------------- */ +/* --- controls ---------------------------------------------- */ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { - int i; - - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - if (qc->id && qc->id == vivi_qctrl[i].id) { - memcpy(qc, &(vivi_qctrl[i]), - sizeof(*qc)); - return (0); - } - + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 200); + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 16); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 127); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); + } return -EINVAL; } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; - int i; - - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - if (ctrl->id == vivi_qctrl[i].id) { - ctrl->value = dev->qctl_regs[i]; - return 0; - } + struct vivi_dev *dev = video_drvdata(file); + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = dev->volume; + return 0; + case V4L2_CID_BRIGHTNESS: + ctrl->value = dev->brightness; + return 0; + case V4L2_CID_CONTRAST: + ctrl->value = dev->contrast; + return 0; + case V4L2_CID_SATURATION: + ctrl->value = dev->saturation; + return 0; + case V4L2_CID_HUE: + ctrl->value = dev->hue; + return 0; + } return -EINVAL; } + static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct vivi_fh *fh = priv; - struct vivi_dev *dev = fh->dev; - int i; - - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - if (ctrl->id == vivi_qctrl[i].id) { - if (ctrl->value < vivi_qctrl[i].minimum || - ctrl->value > vivi_qctrl[i].maximum) { - return -ERANGE; - } - dev->qctl_regs[i] = ctrl->value; - return 0; - } + struct vivi_dev *dev = video_drvdata(file); + struct v4l2_queryctrl qc; + int err; + + qc.id = ctrl->id; + err = vidioc_queryctrl(file, priv, &qc); + if (err < 0) + return err; + if (ctrl->value < qc.minimum || ctrl->value > qc.maximum) + return -ERANGE; + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->value; + return 0; + case V4L2_CID_BRIGHTNESS: + dev->brightness = ctrl->value; + return 0; + case V4L2_CID_CONTRAST: + dev->contrast = ctrl->value; + return 0; + case V4L2_CID_SATURATION: + dev->saturation = ctrl->value; + return 0; + case V4L2_CID_HUE: + dev->hue = ctrl->value; + return 0; + } return -EINVAL; } @@ -1133,134 +1031,58 @@ static int vidioc_s_ctrl(struct file *file, void *priv, File operations for the device ------------------------------------------------------------------*/ -static int vivi_open(struct file *file) -{ - struct vivi_dev *dev = video_drvdata(file); - struct vivi_fh *fh = NULL; - int retval = 0; - - mutex_lock(&dev->mutex); - dev->users++; - - if (dev->users > 1) { - dev->users--; - mutex_unlock(&dev->mutex); - return -EBUSY; - } - - dprintk(dev, 1, "open %s type=%s users=%d\n", - video_device_node_name(dev->vfd), - v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) { - dev->users--; - retval = -ENOMEM; - } - mutex_unlock(&dev->mutex); - - if (retval) - return retval; - - file->private_data = fh; - fh->dev = dev; - - fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fh->fmt = &formats[0]; - fh->width = 640; - fh->height = 480; - - /* Resets frame counters */ - dev->h = 0; - dev->m = 0; - dev->s = 0; - dev->ms = 0; - dev->mv_count = 0; - dev->jiffies = jiffies; - sprintf(dev->timestr, "%02d:%02d:%02d:%03d", - dev->h, dev->m, dev->s, dev->ms); - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &vivi_video_qops, - NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, - sizeof(struct vivi_buffer), fh); - - vivi_start_thread(fh); - - return 0; -} - static ssize_t vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct vivi_fh *fh = file->private_data; + struct vivi_dev *dev = video_drvdata(file); - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0, + vivi_start_generating(file); + return videobuf_read_stream(&dev->vb_vidq, data, count, ppos, 0, file->f_flags & O_NONBLOCK); - } - return 0; } static unsigned int vivi_poll(struct file *file, struct poll_table_struct *wait) { - struct vivi_fh *fh = file->private_data; - struct vivi_dev *dev = fh->dev; - struct videobuf_queue *q = &fh->vb_vidq; + struct vivi_dev *dev = video_drvdata(file); + struct videobuf_queue *q = &dev->vb_vidq; dprintk(dev, 1, "%s\n", __func__); - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return POLLERR; - + vivi_start_generating(file); return videobuf_poll_stream(file, q, wait); } static int vivi_close(struct file *file) { - struct vivi_fh *fh = file->private_data; - struct vivi_dev *dev = fh->dev; - struct vivi_dmaqueue *vidq = &dev->vidq; struct video_device *vdev = video_devdata(file); + struct vivi_dev *dev = video_drvdata(file); - vivi_stop_thread(vidq); - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - kfree(fh); - - mutex_lock(&dev->mutex); - dev->users--; - mutex_unlock(&dev->mutex); - - dprintk(dev, 1, "close called (dev=%s, users=%d)\n", - video_device_node_name(vdev), dev->users); + vivi_stop_generating(file); + dprintk(dev, 1, "close called (dev=%s)\n", + video_device_node_name(vdev)); return 0; } static int vivi_mmap(struct file *file, struct vm_area_struct *vma) { - struct vivi_fh *fh = file->private_data; - struct vivi_dev *dev = fh->dev; + struct vivi_dev *dev = video_drvdata(file); int ret; dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); - ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + ret = videobuf_mmap_mapper(&dev->vb_vidq, vma); dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; } static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, - .open = vivi_open, .release = vivi_close, .read = vivi_read, .poll = vivi_poll, @@ -1282,11 +1104,11 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif @@ -1330,7 +1152,7 @@ static int __init vivi_create_instance(int inst) { struct vivi_dev *dev; struct video_device *vfd; - int ret, i; + int ret; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) @@ -1342,6 +1164,20 @@ static int __init vivi_create_instance(int inst) if (ret) goto free_dev; + dev->fmt = &formats[0]; + dev->width = 640; + dev->height = 480; + dev->volume = 200; + dev->brightness = 127; + dev->contrast = 16; + dev->saturation = 127; + dev->hue = 0; + + videobuf_queue_vmalloc_init(&dev->vb_vidq, &vivi_video_qops, + NULL, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct vivi_buffer), dev); + /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); init_waitqueue_head(&dev->vidq.wq); @@ -1357,6 +1193,7 @@ static int __init vivi_create_instance(int inst) *vfd = vivi_template; vfd->debug = debug; + vfd->v4l2_dev = &dev->v4l2_dev; ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); if (ret < 0) @@ -1364,10 +1201,6 @@ static int __init vivi_create_instance(int inst) video_set_drvdata(vfd, dev); - /* Set all controls to their default value. */ - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - dev->qctl_regs[i] = vivi_qctrl[i].default_value; - /* Now that everything is fine, let's add it to device list */ list_add_tail(&dev->vivi_devlist, &vivi_devlist); @@ -1396,8 +1229,15 @@ free_dev: */ static int __init vivi_init(void) { + const struct font_desc *font = find_font("VGA8x16"); int ret = 0, i; + if (font == NULL) { + printk(KERN_ERR "vivi: could not find font\n"); + return -ENODEV; + } + font8x16 = font->data; + if (n_devs <= 0) n_devs = 1; @@ -1412,7 +1252,7 @@ static int __init vivi_init(void) } if (ret < 0) { - printk(KERN_INFO "Error %d while loading vivi driver\n", ret); + printk(KERN_ERR "vivi: error %d while loading driver\n", ret); return ret; } diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index bf9bf650a317..635420d8d84a 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -1,7 +1,7 @@ /* Winbond w9966cf Webcam parport driver. - Version 0.32 + Version 0.33 Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se> @@ -57,10 +57,12 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/videodev.h> +#include <linux/version.h> +#include <linux/videodev2.h> #include <linux/slab.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> #include <linux/parport.h> /*#define DEBUG*/ /* Undef me for production */ @@ -76,12 +78,12 @@ */ #define W9966_DRIVERNAME "W9966CF Webcam" -#define W9966_MAXCAMS 4 // Maximum number of cameras -#define W9966_RBUFFER 2048 // Read buffer (must be an even number) -#define W9966_SRAMSIZE 131072 // 128kb -#define W9966_SRAMID 0x02 // check w9966cf.pdf +#define W9966_MAXCAMS 4 /* Maximum number of cameras */ +#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */ +#define W9966_SRAMSIZE 131072 /* 128kb */ +#define W9966_SRAMID 0x02 /* check w9966cf.pdf */ -// Empirically determined window limits +/* Empirically determined window limits */ #define W9966_WND_MIN_X 16 #define W9966_WND_MIN_Y 14 #define W9966_WND_MAX_X 705 @@ -89,7 +91,7 @@ #define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X) #define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y) -// Keep track of our current state +/* Keep track of our current state */ #define W9966_STATE_PDEV 0x01 #define W9966_STATE_CLAIMED 0x02 #define W9966_STATE_VDEV 0x04 @@ -101,12 +103,13 @@ #define W9966_I2C_W_DATA 0x02 #define W9966_I2C_W_CLOCK 0x01 -struct w9966_dev { +struct w9966 { + struct v4l2_device v4l2_dev; unsigned char dev_state; unsigned char i2c_state; unsigned short ppmode; - struct parport* pport; - struct pardevice* pdev; + struct parport *pport; + struct pardevice *pdev; struct video_device vdev; unsigned short width; unsigned short height; @@ -114,7 +117,7 @@ struct w9966_dev { signed char contrast; signed char color; signed char hue; - unsigned long in_use; + struct mutex lock; }; /* @@ -127,15 +130,15 @@ MODULE_LICENSE("GPL"); #ifdef MODULE -static const char* pardev[] = {[0 ... W9966_MAXCAMS] = ""}; +static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""}; #else -static const char* pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; +static const char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; #endif module_param_array(pardev, charp, NULL, 0); -MODULE_PARM_DESC(pardev, "pardev: where to search for\n\ -\teach camera. 'aggressive' means brute-force search.\n\ -\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n\ -\tcam 1 to parport3 and search every parport for cam 2 etc..."); +MODULE_PARM_DESC(pardev, "pardev: where to search for\n" + "\teach camera. 'aggressive' means brute-force search.\n" + "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n" + "\tcam 1 to parport3 and search every parport for cam 2 etc..."); static int parmode; module_param(parmode, int, 0); @@ -144,117 +147,49 @@ MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp"); static int video_nr = -1; module_param(video_nr, int, 0); -/* - * Private data - */ - -static struct w9966_dev w9966_cams[W9966_MAXCAMS]; - -/* - * Private function declares - */ - -static inline void w9966_setState(struct w9966_dev* cam, int mask, int val); -static inline int w9966_getState(struct w9966_dev* cam, int mask, int val); -static inline void w9966_pdev_claim(struct w9966_dev *vdev); -static inline void w9966_pdev_release(struct w9966_dev *vdev); - -static int w9966_rReg(struct w9966_dev* cam, int reg); -static int w9966_wReg(struct w9966_dev* cam, int reg, int data); -#if 0 -static int w9966_rReg_i2c(struct w9966_dev* cam, int reg); -#endif -static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data); -static int w9966_findlen(int near, int size, int maxlen); -static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor); -static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h); - -static int w9966_init(struct w9966_dev* cam, struct parport* port); -static void w9966_term(struct w9966_dev* cam); - -static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state); -static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state); -static inline int w9966_i2c_getsda(struct w9966_dev* cam); -static inline int w9966_i2c_getscl(struct w9966_dev* cam); -static int w9966_i2c_wbyte(struct w9966_dev* cam, int data); -#if 0 -static int w9966_i2c_rbyte(struct w9966_dev* cam); -#endif - -static long w9966_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t w9966_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos); - -static int w9966_exclusive_open(struct file *file) -{ - struct w9966_dev *cam = video_drvdata(file); - - return test_and_set_bit(0, &cam->in_use) ? -EBUSY : 0; -} - -static int w9966_exclusive_release(struct file *file) -{ - struct w9966_dev *cam = video_drvdata(file); - - clear_bit(0, &cam->in_use); - return 0; -} - -static const struct v4l2_file_operations w9966_fops = { - .owner = THIS_MODULE, - .open = w9966_exclusive_open, - .release = w9966_exclusive_release, - .ioctl = w9966_v4l_ioctl, - .read = w9966_v4l_read, -}; -static struct video_device w9966_template = { - .name = W9966_DRIVERNAME, - .fops = &w9966_fops, - .release = video_device_release_empty, -}; +static struct w9966 w9966_cams[W9966_MAXCAMS]; /* * Private function defines */ -// Set camera phase flags, so we know what to uninit when terminating -static inline void w9966_setState(struct w9966_dev* cam, int mask, int val) +/* Set camera phase flags, so we know what to uninit when terminating */ +static inline void w9966_set_state(struct w9966 *cam, int mask, int val) { cam->dev_state = (cam->dev_state & ~mask) ^ val; } -// Get camera phase flags -static inline int w9966_getState(struct w9966_dev* cam, int mask, int val) +/* Get camera phase flags */ +static inline int w9966_get_state(struct w9966 *cam, int mask, int val) { return ((cam->dev_state & mask) == val); } -// Claim parport for ourself -static inline void w9966_pdev_claim(struct w9966_dev* cam) +/* Claim parport for ourself */ +static void w9966_pdev_claim(struct w9966 *cam) { - if (w9966_getState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED)) + if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED)) return; parport_claim_or_block(cam->pdev); - w9966_setState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED); + w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED); } -// Release parport for others to use -static inline void w9966_pdev_release(struct w9966_dev* cam) +/* Release parport for others to use */ +static void w9966_pdev_release(struct w9966 *cam) { - if (w9966_getState(cam, W9966_STATE_CLAIMED, 0)) + if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0)) return; parport_release(cam->pdev); - w9966_setState(cam, W9966_STATE_CLAIMED, 0); + w9966_set_state(cam, W9966_STATE_CLAIMED, 0); } -// Read register from W9966 interface-chip -// Expects a claimed pdev -// -1 on error, else register data (byte) -static int w9966_rReg(struct w9966_dev* cam, int reg) +/* Read register from W9966 interface-chip + Expects a claimed pdev + -1 on error, else register data (byte) */ +static int w9966_read_reg(struct w9966 *cam, int reg) { - // ECP, read, regtransfer, REG, REG, REG, REG, REG + /* ECP, read, regtransfer, REG, REG, REG, REG, REG */ const unsigned char addr = 0x80 | (reg & 0x1f); unsigned char val; @@ -270,12 +205,12 @@ static int w9966_rReg(struct w9966_dev* cam, int reg) return val; } -// Write register to W9966 interface-chip -// Expects a claimed pdev -// -1 on error -static int w9966_wReg(struct w9966_dev* cam, int reg, int data) +/* Write register to W9966 interface-chip + Expects a claimed pdev + -1 on error */ +static int w9966_write_reg(struct w9966 *cam, int reg, int data) { - // ECP, write, regtransfer, REG, REG, REG, REG, REG + /* ECP, write, regtransfer, REG, REG, REG, REG, REG */ const unsigned char addr = 0xc0 | (reg & 0x1f); const unsigned char val = data; @@ -291,119 +226,186 @@ static int w9966_wReg(struct w9966_dev* cam, int reg, int data) return 0; } -// Initialize camera device. Setup all internal flags, set a -// default video mode, setup ccd-chip, register v4l device etc.. -// Also used for 'probing' of hardware. -// -1 on error -static int w9966_init(struct w9966_dev* cam, struct parport* port) +/* + * Ugly and primitive i2c protocol functions + */ + +/* Sets the data line on the i2c bus. + Expects a claimed pdev. */ +static void w9966_i2c_setsda(struct w9966 *cam, int state) { - if (cam->dev_state != 0) - return -1; + if (state) + cam->i2c_state |= W9966_I2C_W_DATA; + else + cam->i2c_state &= ~W9966_I2C_W_DATA; - cam->pport = port; - cam->brightness = 128; - cam->contrast = 64; - cam->color = 64; - cam->hue = 0; + w9966_write_reg(cam, 0x18, cam->i2c_state); + udelay(5); +} -// Select requested transfer mode - switch(parmode) - { - default: // Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) - case 0: - if (port->modes & PARPORT_MODE_ECP) - cam->ppmode = IEEE1284_MODE_ECP; - else if (port->modes & PARPORT_MODE_EPP) - cam->ppmode = IEEE1284_MODE_EPP; - else - cam->ppmode = IEEE1284_MODE_ECP; - break; - case 1: // hw- or sw-ecp - cam->ppmode = IEEE1284_MODE_ECP; - break; - case 2: // hw- or sw-epp - cam->ppmode = IEEE1284_MODE_EPP; - break; +/* Get peripheral clock line + Expects a claimed pdev. */ +static int w9966_i2c_getscl(struct w9966 *cam) +{ + const unsigned char state = w9966_read_reg(cam, 0x18); + return ((state & W9966_I2C_R_CLOCK) > 0); +} + +/* Sets the clock line on the i2c bus. + Expects a claimed pdev. -1 on error */ +static int w9966_i2c_setscl(struct w9966 *cam, int state) +{ + unsigned long timeout; + + if (state) + cam->i2c_state |= W9966_I2C_W_CLOCK; + else + cam->i2c_state &= ~W9966_I2C_W_CLOCK; + + w9966_write_reg(cam, 0x18, cam->i2c_state); + udelay(5); + + /* we go to high, we also expect the peripheral to ack. */ + if (state) { + timeout = jiffies + 100; + while (!w9966_i2c_getscl(cam)) { + if (time_after(jiffies, timeout)) + return -1; + } } + return 0; +} -// Tell the parport driver that we exists - cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); - if (cam->pdev == NULL) { - DPRINTF("parport_register_device() failed\n"); - return -1; +#if 0 +/* Get peripheral data line + Expects a claimed pdev. */ +static int w9966_i2c_getsda(struct w9966 *cam) +{ + const unsigned char state = w9966_read_reg(cam, 0x18); + return ((state & W9966_I2C_R_DATA) > 0); +} +#endif + +/* Write a byte with ack to the i2c bus. + Expects a claimed pdev. -1 on error */ +static int w9966_i2c_wbyte(struct w9966 *cam, int data) +{ + int i; + + for (i = 7; i >= 0; i--) { + w9966_i2c_setsda(cam, (data >> i) & 0x01); + + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + w9966_i2c_setscl(cam, 0); } - w9966_setState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); - w9966_pdev_claim(cam); + w9966_i2c_setsda(cam, 1); -// Setup a default capture mode - if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { - DPRINTF("w9966_setup() failed.\n"); + if (w9966_i2c_setscl(cam, 1) == -1) return -1; + w9966_i2c_setscl(cam, 0); + + return 0; +} + +/* Read a data byte with ack from the i2c-bus + Expects a claimed pdev. -1 on error */ +#if 0 +static int w9966_i2c_rbyte(struct w9966 *cam) +{ + unsigned char data = 0x00; + int i; + + w9966_i2c_setsda(cam, 1); + + for (i = 0; i < 8; i++) { + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + data = data << 1; + if (w9966_i2c_getsda(cam)) + data |= 0x01; + + w9966_i2c_setscl(cam, 0); } + return data; +} +#endif - w9966_pdev_release(cam); +/* Read a register from the i2c device. + Expects claimed pdev. -1 on error */ +#if 0 +static int w9966_read_reg_i2c(struct w9966 *cam, int reg) +{ + int data; -// Fill in the video_device struct and register us to v4l - memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device)); - video_set_drvdata(&cam->vdev, cam); + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) + if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || + w9966_i2c_wbyte(cam, reg) == -1) + return -1; + + w9966_i2c_setsda(cam, 1); + if (w9966_i2c_setscl(cam, 1) == -1) return -1; + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); - w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); + if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1) + return -1; + data = w9966_i2c_rbyte(cam); + if (data == -1) + return -1; - // All ok - printk( - "w9966cf: Found and initialized a webcam on %s.\n", - cam->pport->name - ); - return 0; -} + w9966_i2c_setsda(cam, 0); + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + w9966_i2c_setsda(cam, 1); -// Terminate everything gracefully -static void w9966_term(struct w9966_dev* cam) + return data; +} +#endif + +/* Write a register to the i2c device. + Expects claimed pdev. -1 on error */ +static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data) { -// Unregister from v4l - if (w9966_getState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) { - video_unregister_device(&cam->vdev); - w9966_setState(cam, W9966_STATE_VDEV, 0); - } + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); -// Terminate from IEEE1284 mode and release pdev block - if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { - w9966_pdev_claim(cam); - parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT); - w9966_pdev_release(cam); - } + if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || + w9966_i2c_wbyte(cam, reg) == -1 || + w9966_i2c_wbyte(cam, data) == -1) + return -1; -// Unregister from parport - if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { - parport_unregister_device(cam->pdev); - w9966_setState(cam, W9966_STATE_PDEV, 0); - } -} + w9966_i2c_setsda(cam, 0); + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + + w9966_i2c_setsda(cam, 1); + return 0; +} -// Find a good length for capture window (used both for W and H) -// A bit ugly but pretty functional. The capture length -// have to match the downscale +/* Find a good length for capture window (used both for W and H) + A bit ugly but pretty functional. The capture length + have to match the downscale */ static int w9966_findlen(int near, int size, int maxlen) { int bestlen = size; int besterr = abs(near - bestlen); int len; - for(len = size+1;len < maxlen;len++) - { + for (len = size + 1; len < maxlen; len++) { int err; - if ( ((64*size) %len) != 0) + if (((64 * size) % len) != 0) continue; err = abs(near - len); - // Only continue as long as we keep getting better values + /* Only continue as long as we keep getting better values */ if (err > besterr) break; @@ -414,32 +416,32 @@ static int w9966_findlen(int near, int size, int maxlen) return bestlen; } -// Modify capture window (if necessary) -// and calculate downscaling -// Return -1 on error -static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor) +/* Modify capture window (if necessary) + and calculate downscaling + Return -1 on error */ +static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor) { int maxlen = max - min; int len = *end - *beg + 1; int newlen = w9966_findlen(len, size, maxlen); int err = newlen - len; - // Check for bad format + /* Check for bad format */ if (newlen > maxlen || newlen < size) return -1; - // Set factor (6 bit fixed) - *factor = (64*size) / newlen; + /* Set factor (6 bit fixed) */ + *factor = (64 * size) / newlen; if (*factor == 64) - *factor = 0x00; // downscale is disabled + *factor = 0x00; /* downscale is disabled */ else - *factor |= 0x80; // set downscale-enable bit + *factor |= 0x80; /* set downscale-enable bit */ - // Modify old beginning and end + /* Modify old beginning and end */ *beg -= err / 2; *end += err - (err / 2); - // Move window if outside borders + /* Move window if outside borders */ if (*beg < min) { *end += min - *beg; *beg += min - *beg; @@ -452,10 +454,10 @@ static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsig return 0; } -// Setup the cameras capture window etc. -// Expects a claimed pdev -// return -1 on error -static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h) +/* Setup the cameras capture window etc. + Expects a claimed pdev + return -1 on error */ +static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h) { unsigned int i; unsigned int enh_s, enh_e; @@ -469,443 +471,314 @@ static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, in }; - if (w*h*2 > W9966_SRAMSIZE) - { + if (w * h * 2 > W9966_SRAMSIZE) { DPRINTF("capture window exceeds SRAM size!.\n"); - w = 200; h = 160; // Pick default values + w = 200; h = 160; /* Pick default values */ } w &= ~0x1; - if (w < 2) w = 2; - if (h < 1) h = 1; - if (w > W9966_WND_MAX_W) w = W9966_WND_MAX_W; - if (h > W9966_WND_MAX_H) h = W9966_WND_MAX_H; + if (w < 2) + w = 2; + if (h < 1) + h = 1; + if (w > W9966_WND_MAX_W) + w = W9966_WND_MAX_W; + if (h > W9966_WND_MAX_H) + h = W9966_WND_MAX_H; cam->width = w; cam->height = h; enh_s = 0; - enh_e = w*h*2; - -// Modify capture window if necessary and calculate downscaling - if ( - w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || - w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0 - ) return -1; - - DPRINTF( - "%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", - w, h, x1, x2, y1, y2, scale_x&~0x80, scale_y&~0x80 - ); - -// Setup registers - regs[0x00] = 0x00; // Set normal operation - regs[0x01] = 0x18; // Capture mode - regs[0x02] = scale_y; // V-scaling - regs[0x03] = scale_x; // H-scaling - - // Capture window - regs[0x04] = (x1 & 0x0ff); // X-start (8 low bits) - regs[0x05] = (x1 & 0x300)>>8; // X-start (2 high bits) - regs[0x06] = (y1 & 0x0ff); // Y-start (8 low bits) - regs[0x07] = (y1 & 0x300)>>8; // Y-start (2 high bits) - regs[0x08] = (x2 & 0x0ff); // X-end (8 low bits) - regs[0x09] = (x2 & 0x300)>>8; // X-end (2 high bits) - regs[0x0a] = (y2 & 0x0ff); // Y-end (8 low bits) - - regs[0x0c] = W9966_SRAMID; // SRAM-banks (1x 128kb) - - // Enhancement layer - regs[0x0d] = (enh_s& 0x000ff); // Enh. start (0-7) - regs[0x0e] = (enh_s& 0x0ff00)>>8; // Enh. start (8-15) - regs[0x0f] = (enh_s& 0x70000)>>16; // Enh. start (16-17/18??) - regs[0x10] = (enh_e& 0x000ff); // Enh. end (0-7) - regs[0x11] = (enh_e& 0x0ff00)>>8; // Enh. end (8-15) - regs[0x12] = (enh_e& 0x70000)>>16; // Enh. end (16-17/18??) - - // Misc - regs[0x13] = 0x40; // VEE control (raw 4:2:2) - regs[0x17] = 0x00; // ??? - regs[0x18] = cam->i2c_state = 0x00; // Serial bus - regs[0x19] = 0xff; // I/O port direction control - regs[0x1a] = 0xff; // I/O port data register - regs[0x1b] = 0x10; // ??? - - // SAA7111 chip settings + enh_e = w * h * 2; + + /* Modify capture window if necessary and calculate downscaling */ + if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || + w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0) + return -1; + + DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", + w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80); + + /* Setup registers */ + regs[0x00] = 0x00; /* Set normal operation */ + regs[0x01] = 0x18; /* Capture mode */ + regs[0x02] = scale_y; /* V-scaling */ + regs[0x03] = scale_x; /* H-scaling */ + + /* Capture window */ + regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */ + regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */ + regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */ + regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */ + regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */ + regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */ + regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */ + + regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */ + + /* Enhancement layer */ + regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */ + regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */ + regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */ + regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */ + regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */ + regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */ + + /* Misc */ + regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */ + regs[0x17] = 0x00; /* ??? */ + regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */ + regs[0x19] = 0xff; /* I/O port direction control */ + regs[0x1a] = 0xff; /* I/O port data register */ + regs[0x1b] = 0x10; /* ??? */ + + /* SAA7111 chip settings */ saa7111_regs[0x0a] = cam->brightness; saa7111_regs[0x0b] = cam->contrast; saa7111_regs[0x0c] = cam->color; saa7111_regs[0x0d] = cam->hue; -// Reset (ECP-fifo & serial-bus) - if (w9966_wReg(cam, 0x00, 0x03) == -1) + /* Reset (ECP-fifo & serial-bus) */ + if (w9966_write_reg(cam, 0x00, 0x03) == -1) return -1; -// Write regs to w9966cf chip + /* Write regs to w9966cf chip */ for (i = 0; i < 0x1c; i++) - if (w9966_wReg(cam, i, regs[i]) == -1) + if (w9966_write_reg(cam, i, regs[i]) == -1) return -1; -// Write regs to saa7111 chip + /* Write regs to saa7111 chip */ for (i = 0; i < 0x20; i++) - if (w9966_wReg_i2c(cam, i, saa7111_regs[i]) == -1) + if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1) return -1; return 0; } /* - * Ugly and primitive i2c protocol functions + * Video4linux interfacing */ -// Sets the data line on the i2c bus. -// Expects a claimed pdev. -static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state) +static int cam_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) { - if (state) - cam->i2c_state |= W9966_I2C_W_DATA; - else - cam->i2c_state &= ~W9966_I2C_W_DATA; + struct w9966 *cam = video_drvdata(file); - w9966_wReg(cam, 0x18, cam->i2c_state); - udelay(5); + strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card)); + strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); + vcap->version = KERNEL_VERSION(0, 33, 0); + vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; } -// Get peripheral clock line -// Expects a claimed pdev. -static inline int w9966_i2c_getscl(struct w9966_dev* cam) +static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) { - const unsigned char state = w9966_rReg(cam, 0x18); - return ((state & W9966_I2C_R_CLOCK) > 0); + if (vin->index > 0) + return -EINVAL; + strlcpy(vin->name, "Camera", sizeof(vin->name)); + vin->type = V4L2_INPUT_TYPE_CAMERA; + vin->audioset = 0; + vin->tuner = 0; + vin->std = 0; + vin->status = 0; + return 0; } -// Sets the clock line on the i2c bus. -// Expects a claimed pdev. -1 on error -static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state) +static int cam_g_input(struct file *file, void *fh, unsigned int *inp) { - unsigned long timeout; - - if (state) - cam->i2c_state |= W9966_I2C_W_CLOCK; - else - cam->i2c_state &= ~W9966_I2C_W_CLOCK; - - w9966_wReg(cam, 0x18, cam->i2c_state); - udelay(5); - - // we go to high, we also expect the peripheral to ack. - if (state) { - timeout = jiffies + 100; - while (!w9966_i2c_getscl(cam)) { - if (time_after(jiffies, timeout)) - return -1; - } - } + *inp = 0; return 0; } -// Get peripheral data line -// Expects a claimed pdev. -static inline int w9966_i2c_getsda(struct w9966_dev* cam) +static int cam_s_input(struct file *file, void *fh, unsigned int inp) { - const unsigned char state = w9966_rReg(cam, 0x18); - return ((state & W9966_I2C_R_DATA) > 0); + return (inp > 0) ? -EINVAL : 0; } -// Write a byte with ack to the i2c bus. -// Expects a claimed pdev. -1 on error -static int w9966_i2c_wbyte(struct w9966_dev* cam, int data) +static int cam_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) { - int i; - for (i = 7; i >= 0; i--) - { - w9966_i2c_setsda(cam, (data >> i) & 0x01); - - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setscl(cam, 0); + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); } - - w9966_i2c_setsda(cam, 1); - - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setscl(cam, 0); - - return 0; + return -EINVAL; } -// Read a data byte with ack from the i2c-bus -// Expects a claimed pdev. -1 on error -#if 0 -static int w9966_i2c_rbyte(struct w9966_dev* cam) +static int cam_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { - unsigned char data = 0x00; - int i; - - w9966_i2c_setsda(cam, 1); + struct w9966 *cam = video_drvdata(file); + int ret = 0; - for (i = 0; i < 8; i++) - { - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - data = data << 1; - if (w9966_i2c_getsda(cam)) - data |= 0x01; - - w9966_i2c_setscl(cam, 0); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = cam->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = cam->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = cam->color; + break; + case V4L2_CID_HUE: + ctrl->value = cam->hue; + break; + default: + ret = -EINVAL; + break; } - return data; + return ret; } -#endif -// Read a register from the i2c device. -// Expects claimed pdev. -1 on error -#if 0 -static int w9966_rReg_i2c(struct w9966_dev* cam, int reg) +static int cam_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { - int data; - - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); - - if ( - w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || - w9966_i2c_wbyte(cam, reg) == -1 - ) - return -1; - - w9966_i2c_setsda(cam, 1); - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); + struct w9966 *cam = video_drvdata(file); + int ret = 0; - if ( - w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1 || - (data = w9966_i2c_rbyte(cam)) == -1 - ) - return -1; + mutex_lock(&cam->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cam->brightness = ctrl->value; + break; + case V4L2_CID_CONTRAST: + cam->contrast = ctrl->value; + break; + case V4L2_CID_SATURATION: + cam->color = ctrl->value; + break; + case V4L2_CID_HUE: + cam->hue = ctrl->value; + break; + default: + ret = -EINVAL; + break; + } - w9966_i2c_setsda(cam, 0); + if (ret == 0) { + w9966_pdev_claim(cam); - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setsda(cam, 1); + if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 || + w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 || + w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 || + w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) { + ret = -EIO; + } - return data; + w9966_pdev_release(cam); + } + mutex_unlock(&cam->lock); + return ret; } -#endif -// Write a register to the i2c device. -// Expects claimed pdev. -1 on error -static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data) +static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); - - if ( - w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || - w9966_i2c_wbyte(cam, reg) == -1 || - w9966_i2c_wbyte(cam, data) == -1 - ) - return -1; - - w9966_i2c_setsda(cam, 0); - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - - w9966_i2c_setsda(cam, 1); - + struct w9966 *cam = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + pix->width = cam->width; + pix->height = cam->height; + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * cam->width; + pix->sizeimage = 2 * cam->width * cam->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } -/* - * Video4linux interfacing - */ - -static long w9966_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct w9966_dev *cam = video_drvdata(file); - - switch(cmd) - { - case VIDIOCGCAP: - { - static struct video_capability vcap = { - .name = W9966_DRIVERNAME, - .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, - .channels = 1, - .maxwidth = W9966_WND_MAX_W, - .maxheight = W9966_WND_MAX_H, - .minwidth = 2, - .minheight = 1, - }; - struct video_capability *cap = arg; - *cap = vcap; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *vch = arg; - if(vch->channel != 0) // We only support one channel (#0) - return -EINVAL; - memset(vch,0,sizeof(*vch)); - strcpy(vch->name, "CCD-input"); - vch->type = VIDEO_TYPE_CAMERA; - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *vch = arg; - if(vch->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *vtune = arg; - if(vtune->tuner != 0) - return -EINVAL; - strcpy(vtune->name, "no tuner"); - vtune->rangelow = 0; - vtune->rangehigh = 0; - vtune->flags = VIDEO_TUNER_NORM; - vtune->mode = VIDEO_MODE_AUTO; - vtune->signal = 0xffff; - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *vtune = arg; - if (vtune->tuner != 0) - return -EINVAL; - if (vtune->mode != VIDEO_MODE_AUTO) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture vpic = { - cam->brightness << 8, // brightness - (cam->hue + 128) << 8, // hue - cam->color << 9, // color - cam->contrast << 9, // contrast - 0x8000, // whiteness - 16, VIDEO_PALETTE_YUV422// bpp, palette format - }; - struct video_picture *pic = arg; - *pic = vpic; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *vpic = arg; - if (vpic->depth != 16 || (vpic->palette != VIDEO_PALETTE_YUV422 && vpic->palette != VIDEO_PALETTE_YUYV)) - return -EINVAL; - - cam->brightness = vpic->brightness >> 8; - cam->hue = (vpic->hue >> 8) - 128; - cam->color = vpic->colour >> 9; - cam->contrast = vpic->contrast >> 9; +static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + if (pix->width < 2) + pix->width = 2; + if (pix->height < 1) + pix->height = 1; + if (pix->width > W9966_WND_MAX_W) + pix->width = W9966_WND_MAX_W; + if (pix->height > W9966_WND_MAX_H) + pix->height = W9966_WND_MAX_H; + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * pix->width; + pix->sizeimage = 2 * pix->width * pix->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} - w9966_pdev_claim(cam); +static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct w9966 *cam = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int ret = cam_try_fmt_vid_cap(file, fh, fmt); - if ( - w9966_wReg_i2c(cam, 0x0a, cam->brightness) == -1 || - w9966_wReg_i2c(cam, 0x0b, cam->contrast) == -1 || - w9966_wReg_i2c(cam, 0x0c, cam->color) == -1 || - w9966_wReg_i2c(cam, 0x0d, cam->hue) == -1 - ) { - w9966_pdev_release(cam); - return -EIO; - } + if (ret) + return ret; - w9966_pdev_release(cam); - return 0; - } - case VIDIOCSWIN: - { - int ret; - struct video_window *vwin = arg; - - if (vwin->flags != 0) - return -EINVAL; - if (vwin->clipcount != 0) - return -EINVAL; - if (vwin->width < 2 || vwin->width > W9966_WND_MAX_W) - return -EINVAL; - if (vwin->height < 1 || vwin->height > W9966_WND_MAX_H) - return -EINVAL; - - // Update camera regs - w9966_pdev_claim(cam); - ret = w9966_setup(cam, 0, 0, 1023, 1023, vwin->width, vwin->height); - w9966_pdev_release(cam); + mutex_lock(&cam->lock); + /* Update camera regs */ + w9966_pdev_claim(cam); + ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height); + w9966_pdev_release(cam); + mutex_unlock(&cam->lock); + return ret; +} - if (ret != 0) { - DPRINTF("VIDIOCSWIN: w9966_setup() failed.\n"); - return -EIO; - } +static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) +{ + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "YUV 4:2:2", V4L2_PIX_FMT_YUYV, + { 0, 0, 0, 0 } + }, + }; + enum v4l2_buf_type type = fmt->type; - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vwin = arg; - memset(vwin, 0, sizeof(*vwin)); - vwin->width = cam->width; - vwin->height = cam->height; - return 0; - } - // Unimplemented - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: + if (fmt->index > 0) return -EINVAL; - default: - return -ENOIOCTLCMD; - } - return 0; -} -static long w9966_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, w9966_v4l_do_ioctl); + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; } -// Capture data +/* Capture data */ static ssize_t w9966_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { - struct w9966_dev *cam = video_drvdata(file); - unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000 + struct w9966 *cam = video_drvdata(file); + unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */ unsigned char __user *dest = (unsigned char __user *)buf; unsigned long dleft = count; unsigned char *tbuf; - // Why would anyone want more than this?? + /* Why would anyone want more than this?? */ if (count > cam->width * cam->height * 2) return -EINVAL; + mutex_lock(&cam->lock); w9966_pdev_claim(cam); - w9966_wReg(cam, 0x00, 0x02); // Reset ECP-FIFO buffer - w9966_wReg(cam, 0x00, 0x00); // Return to normal operation - w9966_wReg(cam, 0x01, 0x98); // Enable capture - - // write special capture-addr and negotiate into data transfer - if ( - (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0 )|| - (parport_write(cam->pport, &addr, 1) != 1 )|| - (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0 ) - ) { + w9966_write_reg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */ + w9966_write_reg(cam, 0x00, 0x00); /* Return to normal operation */ + w9966_write_reg(cam, 0x01, 0x98); /* Enable capture */ + + /* write special capture-addr and negotiate into data transfer */ + if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) || + (parport_write(cam->pport, &addr, 1) != 1) || + (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) { w9966_pdev_release(cam); + mutex_unlock(&cam->lock); return -EFAULT; } @@ -915,8 +788,7 @@ static ssize_t w9966_v4l_read(struct file *file, char __user *buf, goto out; } - while(dleft > 0) - { + while (dleft > 0) { unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft; if (parport_read(cam->pport, tbuf, tsize) < tsize) { @@ -931,43 +803,167 @@ static ssize_t w9966_v4l_read(struct file *file, char __user *buf, dleft -= tsize; } - w9966_wReg(cam, 0x01, 0x18); // Disable capture + w9966_write_reg(cam, 0x01, 0x18); /* Disable capture */ out: kfree(tbuf); w9966_pdev_release(cam); + mutex_unlock(&cam->lock); return count; } +static const struct v4l2_file_operations w9966_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .read = w9966_v4l_read, +}; + +static const struct v4l2_ioctl_ops w9966_ioctl_ops = { + .vidioc_querycap = cam_querycap, + .vidioc_g_input = cam_g_input, + .vidioc_s_input = cam_s_input, + .vidioc_enum_input = cam_enum_input, + .vidioc_queryctrl = cam_queryctrl, + .vidioc_g_ctrl = cam_g_ctrl, + .vidioc_s_ctrl = cam_s_ctrl, + .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap, +}; + -// Called once for every parport on init +/* Initialize camera device. Setup all internal flags, set a + default video mode, setup ccd-chip, register v4l device etc.. + Also used for 'probing' of hardware. + -1 on error */ +static int w9966_init(struct w9966 *cam, struct parport *port) +{ + struct v4l2_device *v4l2_dev = &cam->v4l2_dev; + + if (cam->dev_state != 0) + return -1; + + strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name)); + + if (v4l2_device_register(NULL, v4l2_dev) < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return -1; + } + cam->pport = port; + cam->brightness = 128; + cam->contrast = 64; + cam->color = 64; + cam->hue = 0; + + /* Select requested transfer mode */ + switch (parmode) { + default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */ + case 0: + if (port->modes & PARPORT_MODE_ECP) + cam->ppmode = IEEE1284_MODE_ECP; + else if (port->modes & PARPORT_MODE_EPP) + cam->ppmode = IEEE1284_MODE_EPP; + else + cam->ppmode = IEEE1284_MODE_ECP; + break; + case 1: /* hw- or sw-ecp */ + cam->ppmode = IEEE1284_MODE_ECP; + break; + case 2: /* hw- or sw-epp */ + cam->ppmode = IEEE1284_MODE_EPP; + break; + } + + /* Tell the parport driver that we exists */ + cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); + if (cam->pdev == NULL) { + DPRINTF("parport_register_device() failed\n"); + return -1; + } + w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); + + w9966_pdev_claim(cam); + + /* Setup a default capture mode */ + if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { + DPRINTF("w9966_setup() failed.\n"); + return -1; + } + + w9966_pdev_release(cam); + + /* Fill in the video_device struct and register us to v4l */ + strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name)); + cam->vdev.v4l2_dev = v4l2_dev; + cam->vdev.fops = &w9966_fops; + cam->vdev.ioctl_ops = &w9966_ioctl_ops; + cam->vdev.release = video_device_release_empty; + video_set_drvdata(&cam->vdev, cam); + + mutex_init(&cam->lock); + + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) + return -1; + + w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); + + /* All ok */ + v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n", + cam->pport->name); + return 0; +} + + +/* Terminate everything gracefully */ +static void w9966_term(struct w9966 *cam) +{ + /* Unregister from v4l */ + if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) { + video_unregister_device(&cam->vdev); + w9966_set_state(cam, W9966_STATE_VDEV, 0); + } + + /* Terminate from IEEE1284 mode and release pdev block */ + if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { + w9966_pdev_claim(cam); + parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT); + w9966_pdev_release(cam); + } + + /* Unregister from parport */ + if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { + parport_unregister_device(cam->pdev); + w9966_set_state(cam, W9966_STATE_PDEV, 0); + } +} + + +/* Called once for every parport on init */ static void w9966_attach(struct parport *port) { int i; - for (i = 0; i < W9966_MAXCAMS; i++) - { - if (w9966_cams[i].dev_state != 0) // Cam is already assigned + for (i = 0; i < W9966_MAXCAMS; i++) { + if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */ continue; - if ( - strcmp(pardev[i], "aggressive") == 0 || - strcmp(pardev[i], port->name) == 0 - ) { + if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) { if (w9966_init(&w9966_cams[i], port) != 0) - w9966_term(&w9966_cams[i]); - break; // return + w9966_term(&w9966_cams[i]); + break; /* return */ } } } -// Called once for every parport on termination +/* Called once for every parport on termination */ static void w9966_detach(struct parport *port) { int i; + for (i = 0; i < W9966_MAXCAMS; i++) - if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) - w9966_term(&w9966_cams[i]); + if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) + w9966_term(&w9966_cams[i]); } @@ -977,17 +973,18 @@ static struct parport_driver w9966_ppd = { .detach = w9966_detach, }; -// Module entry point +/* Module entry point */ static int __init w9966_mod_init(void) { int i; + for (i = 0; i < W9966_MAXCAMS; i++) w9966_cams[i].dev_state = 0; return parport_register_driver(&w9966_ppd); } -// Module cleanup +/* Module cleanup */ static void __exit w9966_mod_term(void) { parport_unregister_driver(&w9966_ppd); diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index e44e4b5f3e50..bb51cfb0c647 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -1153,7 +1153,7 @@ zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { if (ctrl.id == s->qctrl[i].id) { if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) return -EINVAL; @@ -1163,7 +1163,9 @@ zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) ctrl.value -= ctrl.value % s->qctrl[i].step; break; } - + } + if (i == ARRAY_SIZE(s->qctrl)) + return -EINVAL; if ((err = s->set_ctrl(cam, &ctrl))) return err; diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h index 3a408de91b9c..0be783c203f7 100644 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ b/drivers/media/video/zc0301/zc0301_sensor.h @@ -58,7 +58,7 @@ zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); .idProduct = (prod), \ .bInterfaceClass = (intclass) -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE +#if !defined CONFIG_USB_GSPCA_ZC3XX && !defined CONFIG_USB_GSPCA_ZC3XX_MODULE #define ZC0301_ID_TABLE \ static const struct usb_device_id zc0301_id_table[] = { \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h index cb1de7ea197a..8997add1248e 100644 --- a/drivers/media/video/zoran/zoran.h +++ b/drivers/media/video/zoran/zoran.h @@ -33,6 +33,10 @@ #include <media/v4l2-device.h> +#define ZORAN_VIDMODE_PAL 0 +#define ZORAN_VIDMODE_NTSC 1 +#define ZORAN_VIDMODE_SECAM 2 + struct zoran_requestbuffers { unsigned long count; /* Number of buffers for MJPEG grabbing */ unsigned long size; /* Size PER BUFFER in bytes */ @@ -48,7 +52,7 @@ struct zoran_sync { struct zoran_status { int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ int signal; /* Returned: 1 if valid video signal detected */ - int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int norm; /* Returned: ZORAN_VIDMODE_PAL or ZORAN_VIDMODE_NTSC */ int color; /* Returned: 1 if color signal detected */ }; @@ -62,7 +66,7 @@ struct zoran_params { /* Main control parameters */ int input; /* Input channel: 0 = Composite, 1 = S-VHS */ - int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int norm; /* Norm: ZORAN_VIDMODE_PAL or ZORAN_VIDMODE_NTSC */ int decimation; /* decimation of captured video, * enlargement of video played back. * Valid values are 1, 2, 4 or 0. @@ -131,13 +135,13 @@ struct zoran_params { /* Private IOCTL to set up for displaying MJPEG */ -#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params) -#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params) -#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers) -#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) -#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) -#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync) -#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status) +#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOC_PRIVATE+0, struct zoran_params) +#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOC_PRIVATE+1, struct zoran_params) +#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOC_PRIVATE+2, struct zoran_requestbuffers) +#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOC_PRIVATE+3, int) +#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOC_PRIVATE+4, int) +#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOC_PRIVATE+5, struct zoran_sync) +#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOC_PRIVATE+6, struct zoran_status) #ifdef __KERNEL__ @@ -401,7 +405,7 @@ struct zoran { spinlock_t spinlock; /* Spinlock */ /* Video for Linux parameters */ - int input; /* card's norm and input - norm=VIDEO_MODE_* */ + int input; /* card's norm and input */ v4l2_std_id norm; /* Current buffer params */ diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index ec41303544e5..6f89d0a096ea 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -60,7 +60,7 @@ #include <linux/spinlock.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include "videocodec.h" @@ -1549,11 +1549,11 @@ static long zoran_default(struct file *file, void *__fh, int cmd, void *arg) mutex_lock(&zr->resource_lock); if (zr->norm & V4L2_STD_NTSC) - bparams->norm = VIDEO_MODE_NTSC; - else if (zr->norm & V4L2_STD_PAL) - bparams->norm = VIDEO_MODE_PAL; + bparams->norm = ZORAN_VIDMODE_NTSC; + else if (zr->norm & V4L2_STD_SECAM) + bparams->norm = ZORAN_VIDMODE_SECAM; else - bparams->norm = VIDEO_MODE_SECAM; + bparams->norm = ZORAN_VIDMODE_PAL; bparams->input = zr->input; @@ -1789,11 +1789,11 @@ gstat_unlock_and_return: bstat->signal = (status & V4L2_IN_ST_NO_SIGNAL) ? 0 : 1; if (norm & V4L2_STD_NTSC) - bstat->norm = VIDEO_MODE_NTSC; + bstat->norm = ZORAN_VIDMODE_NTSC; else if (norm & V4L2_STD_SECAM) - bstat->norm = VIDEO_MODE_SECAM; + bstat->norm = ZORAN_VIDMODE_SECAM; else - bstat->norm = VIDEO_MODE_PAL; + bstat->norm = ZORAN_VIDMODE_PAL; bstat->color = (status & V4L2_IN_ST_NO_COLOR) ? 0 : 1; diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 3d4bac252902..a82b5bd18d26 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -376,8 +376,8 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, if (*count == 0) *count = ZR364XX_DEF_BUFS; - while (*size * (*count) > ZR364XX_DEF_BUFS * 1024 * 1024) - (*count)--; + if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) + *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; return 0; } diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c index 11073fa3d9f4..d33693c13368 100644 --- a/drivers/message/i2o/i2o_config.c +++ b/drivers/message/i2o/i2o_config.c @@ -314,22 +314,22 @@ static int i2o_cfg_swul(unsigned long arg) int ret = 0; if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) - goto return_fault; + return -EFAULT; if (get_user(swlen, kxfer.swlen) < 0) - goto return_fault; + return -EFAULT; if (get_user(maxfrag, kxfer.maxfrag) < 0) - goto return_fault; + return -EFAULT; if (get_user(curfrag, kxfer.curfrag) < 0) - goto return_fault; + return -EFAULT; if (curfrag == maxfrag) fragsize = swlen - (maxfrag - 1) * 8192; if (!kxfer.buf) - goto return_fault; + return -EFAULT; c = i2o_find_iop(kxfer.iop); if (!c) @@ -373,12 +373,8 @@ static int i2o_cfg_swul(unsigned long arg) i2o_dma_free(&c->pdev->dev, &buffer); - return_ret: return ret; - return_fault: - ret = -EFAULT; - goto return_ret; -}; +} static int i2o_cfg_swdel(unsigned long arg) { diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2a5a0b78f84e..de3e74cde51c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -53,6 +53,10 @@ config MFD_SH_MOBILE_SDHI This driver supports the SDHI hardware block found in many SuperH Mobile SoCs. +config MFD_DAVINCI_VOICECODEC + tristate + select MFD_CORE + config MFD_DM355EVM_MSP bool "DaVinci DM355 EVM microcontroller" depends on I2C && MACH_DAVINCI_DM355_EVM @@ -297,9 +301,9 @@ config MFD_WM8350_I2C selected to enable support for the functionality of the chip. config MFD_WM8994 - tristate "Support Wolfson Microelectronics WM8994" + bool "Support Wolfson Microelectronics WM8994" select MFD_CORE - depends on I2C + depends on I2C=y && GENERIC_HARDIRQS help The WM8994 is a highly integrated hi-fi CODEC designed for smartphone applicatiosn. As well as audio functionality it diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 22715add99a7..87935f967aa0 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o +obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o @@ -25,7 +26,7 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o wm8350-objs += wm8350-irq.o obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o -obj-$(CONFIG_MFD_WM8994) += wm8994-core.o +obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_MENELAUS) += menelaus.o diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c new file mode 100644 index 000000000000..3e75f02e4778 --- /dev/null +++ b/drivers/mfd/davinci_voicecodec.c @@ -0,0 +1,190 @@ +/* + * DaVinci Voice Codec Core Interface for TI platforms + * + * Copyright (C) 2010 Texas Instruments, Inc + * + * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> + +#include <sound/pcm.h> + +#include <linux/mfd/davinci_voicecodec.h> + +u32 davinci_vc_read(struct davinci_vc *davinci_vc, int reg) +{ + return __raw_readl(davinci_vc->base + reg); +} + +void davinci_vc_write(struct davinci_vc *davinci_vc, + int reg, u32 val) +{ + __raw_writel(val, davinci_vc->base + reg); +} + +static int __init davinci_vc_probe(struct platform_device *pdev) +{ + struct davinci_vc *davinci_vc; + struct resource *res, *mem; + struct mfd_cell *cell = NULL; + int ret; + + davinci_vc = kzalloc(sizeof(struct davinci_vc), GFP_KERNEL); + if (!davinci_vc) { + dev_dbg(&pdev->dev, + "could not allocate memory for private data\n"); + return -ENOMEM; + } + + davinci_vc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(davinci_vc->clk)) { + dev_dbg(&pdev->dev, + "could not get the clock for voice codec\n"); + ret = -ENODEV; + goto fail1; + } + clk_enable(davinci_vc->clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no mem resource\n"); + ret = -ENODEV; + goto fail2; + } + + davinci_vc->pbase = res->start; + davinci_vc->base_size = resource_size(res); + + mem = request_mem_region(davinci_vc->pbase, davinci_vc->base_size, + pdev->name); + if (!mem) { + dev_err(&pdev->dev, "VCIF region already claimed\n"); + ret = -EBUSY; + goto fail2; + } + + davinci_vc->base = ioremap(davinci_vc->pbase, davinci_vc->base_size); + if (!davinci_vc->base) { + dev_err(&pdev->dev, "can't ioremap mem resource.\n"); + ret = -ENOMEM; + goto fail3; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, "no DMA resource\n"); + return -ENXIO; + } + + davinci_vc->davinci_vcif.dma_tx_channel = res->start; + davinci_vc->davinci_vcif.dma_tx_addr = + (dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_WFIFO); + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, "no DMA resource\n"); + return -ENXIO; + } + + davinci_vc->davinci_vcif.dma_rx_channel = res->start; + davinci_vc->davinci_vcif.dma_rx_addr = + (dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_RFIFO); + + davinci_vc->dev = &pdev->dev; + davinci_vc->pdev = pdev; + + /* Voice codec interface client */ + cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL]; + cell->name = "davinci_vcif"; + cell->driver_data = davinci_vc; + + /* Voice codec CQ93VC client */ + cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL]; + cell->name = "cq93vc"; + cell->driver_data = davinci_vc; + + ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells, + DAVINCI_VC_CELLS, NULL, 0); + if (ret != 0) { + dev_err(&pdev->dev, "fail to register client devices\n"); + goto fail4; + } + + return 0; + +fail4: + iounmap(davinci_vc->base); +fail3: + release_mem_region(davinci_vc->pbase, davinci_vc->base_size); +fail2: + clk_disable(davinci_vc->clk); + clk_put(davinci_vc->clk); + davinci_vc->clk = NULL; +fail1: + kfree(davinci_vc); + + return ret; +} + +static int __devexit davinci_vc_remove(struct platform_device *pdev) +{ + struct davinci_vc *davinci_vc = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + + iounmap(davinci_vc->base); + release_mem_region(davinci_vc->pbase, davinci_vc->base_size); + + clk_disable(davinci_vc->clk); + clk_put(davinci_vc->clk); + davinci_vc->clk = NULL; + + kfree(davinci_vc); + + return 0; +} + +static struct platform_driver davinci_vc_driver = { + .driver = { + .name = "davinci_voicecodec", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(davinci_vc_remove), +}; + +static int __init davinci_vc_init(void) +{ + return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe); +} +module_init(davinci_vc_init); + +static void __exit davinci_vc_exit(void) +{ + platform_driver_unregister(&davinci_vc_driver); +} +module_exit(davinci_vc_exit); + +MODULE_AUTHOR("Miguel Aguilar"); +MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 562cd4935e17..720e099e506d 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -109,7 +109,7 @@ #endif #if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\ - defined(CONFIG_SND_SOC_TWL6030) || defined(CONFIG_SND_SOC_TWL6030_MODULE) + defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE) #define twl_has_codec() true #else #define twl_has_codec() false @@ -708,7 +708,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) /* Phoenix*/ if (twl_has_codec() && pdata->codec && twl_class_is_6030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl6030_codec", + child = add_child(sub_chip_id, "twl6040_codec", pdata->codec, sizeof(*pdata->codec), false, 0, 0); if (IS_ERR(child)) diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index 301327697117..4c1122ceb443 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -21,6 +21,7 @@ #include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/pdata.h> +#include <linux/mfd/wm831x/gpio.h> #include <linux/mfd/wm831x/irq.h> #include <linux/delay.h> @@ -388,12 +389,41 @@ static void wm831x_irq_mask(unsigned int irq) wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; } +static int wm831x_irq_set_type(unsigned int irq, unsigned int type) +{ + struct wm831x *wm831x = get_irq_chip_data(irq); + int val; + + irq = irq - wm831x->irq_base; + + if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) + return -EINVAL; + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + val = WM831X_GPN_INT_MODE; + break; + case IRQ_TYPE_EDGE_RISING: + val = WM831X_GPN_POL; + break; + case IRQ_TYPE_EDGE_FALLING: + val = 0; + break; + default: + return -EINVAL; + } + + return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq, + WM831X_GPN_INT_MODE | WM831X_GPN_POL, val); +} + static struct irq_chip wm831x_irq_chip = { .name = "wm831x", .bus_lock = wm831x_irq_lock, .bus_sync_unlock = wm831x_irq_sync_unlock, .mask = wm831x_irq_mask, .unmask = wm831x_irq_unmask, + .set_type = wm831x_irq_set_type, }; /* The processing of the primary interrupt occurs in a thread so that diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index cc524df10aa1..ec71c9368906 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -173,9 +173,34 @@ static struct mfd_cell wm8994_regulator_devs[] = { { .name = "wm8994-ldo", .id = 2 }, }; +static struct resource wm8994_codec_resources[] = { + { + .start = WM8994_IRQ_TEMP_SHUT, + .end = WM8994_IRQ_TEMP_WARN, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource wm8994_gpio_resources[] = { + { + .start = WM8994_IRQ_GPIO(1), + .end = WM8994_IRQ_GPIO(11), + .flags = IORESOURCE_IRQ, + }, +}; + static struct mfd_cell wm8994_devs[] = { - { .name = "wm8994-codec" }, - { .name = "wm8994-gpio" }, + { + .name = "wm8994-codec", + .num_resources = ARRAY_SIZE(wm8994_codec_resources), + .resources = wm8994_codec_resources, + }, + + { + .name = "wm8994-gpio", + .num_resources = ARRAY_SIZE(wm8994_gpio_resources), + .resources = wm8994_gpio_resources, + }, }; /* @@ -236,6 +261,11 @@ static int wm8994_device_resume(struct device *dev) return ret; } + ret = wm8994_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK, + WM8994_NUM_IRQ_REGS * 2, &wm8994->irq_masks_cur); + if (ret < 0) + dev_err(dev, "Failed to restore interrupt masks: %d\n", ret); + ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2, &wm8994->ldo_regs); if (ret < 0) @@ -348,6 +378,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq) if (pdata) { + wm8994->irq_base = pdata->irq_base; wm8994->gpio_base = pdata->gpio_base; /* GPIO configuration is only applied if it's non-zero */ @@ -375,16 +406,20 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq) WM8994_LDO1_DISCH, 0); } + wm8994_irq_init(wm8994); + ret = mfd_add_devices(wm8994->dev, -1, wm8994_devs, ARRAY_SIZE(wm8994_devs), NULL, 0); if (ret != 0) { dev_err(wm8994->dev, "Failed to add children: %d\n", ret); - goto err_enable; + goto err_irq; } return 0; +err_irq: + wm8994_irq_exit(wm8994); err_enable: regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies); @@ -401,6 +436,7 @@ err: static void wm8994_device_exit(struct wm8994 *wm8994) { mfd_remove_devices(wm8994->dev); + wm8994_irq_exit(wm8994); regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies); regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies); @@ -469,6 +505,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, wm8994->control_data = i2c; wm8994->read_dev = wm8994_i2c_read_device; wm8994->write_dev = wm8994_i2c_write_device; + wm8994->irq = i2c->irq; return wm8994_device_init(wm8994, id->driver_data, i2c->irq); } diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c new file mode 100644 index 000000000000..8400eb1ee5db --- /dev/null +++ b/drivers/mfd/wm8994-irq.c @@ -0,0 +1,310 @@ +/* + * wm8994-irq.c -- Interrupt controller support for Wolfson WM8994 + * + * Copyright 2010 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/mfd/core.h> +#include <linux/interrupt.h> + +#include <linux/mfd/wm8994/core.h> +#include <linux/mfd/wm8994/registers.h> + +#include <linux/delay.h> + +struct wm8994_irq_data { + int reg; + int mask; +}; + +static struct wm8994_irq_data wm8994_irqs[] = { + [WM8994_IRQ_TEMP_SHUT] = { + .reg = 2, + .mask = WM8994_TEMP_SHUT_EINT, + }, + [WM8994_IRQ_MIC1_DET] = { + .reg = 2, + .mask = WM8994_MIC1_DET_EINT, + }, + [WM8994_IRQ_MIC1_SHRT] = { + .reg = 2, + .mask = WM8994_MIC1_SHRT_EINT, + }, + [WM8994_IRQ_MIC2_DET] = { + .reg = 2, + .mask = WM8994_MIC2_DET_EINT, + }, + [WM8994_IRQ_MIC2_SHRT] = { + .reg = 2, + .mask = WM8994_MIC2_SHRT_EINT, + }, + [WM8994_IRQ_FLL1_LOCK] = { + .reg = 2, + .mask = WM8994_FLL1_LOCK_EINT, + }, + [WM8994_IRQ_FLL2_LOCK] = { + .reg = 2, + .mask = WM8994_FLL2_LOCK_EINT, + }, + [WM8994_IRQ_SRC1_LOCK] = { + .reg = 2, + .mask = WM8994_SRC1_LOCK_EINT, + }, + [WM8994_IRQ_SRC2_LOCK] = { + .reg = 2, + .mask = WM8994_SRC2_LOCK_EINT, + }, + [WM8994_IRQ_AIF1DRC1_SIG_DET] = { + .reg = 2, + .mask = WM8994_AIF1DRC1_SIG_DET, + }, + [WM8994_IRQ_AIF1DRC2_SIG_DET] = { + .reg = 2, + .mask = WM8994_AIF1DRC2_SIG_DET_EINT, + }, + [WM8994_IRQ_AIF2DRC_SIG_DET] = { + .reg = 2, + .mask = WM8994_AIF2DRC_SIG_DET_EINT, + }, + [WM8994_IRQ_FIFOS_ERR] = { + .reg = 2, + .mask = WM8994_FIFOS_ERR_EINT, + }, + [WM8994_IRQ_WSEQ_DONE] = { + .reg = 2, + .mask = WM8994_WSEQ_DONE_EINT, + }, + [WM8994_IRQ_DCS_DONE] = { + .reg = 2, + .mask = WM8994_DCS_DONE_EINT, + }, + [WM8994_IRQ_TEMP_WARN] = { + .reg = 2, + .mask = WM8994_TEMP_WARN_EINT, + }, + [WM8994_IRQ_GPIO(1)] = { + .reg = 1, + .mask = WM8994_GP1_EINT, + }, + [WM8994_IRQ_GPIO(2)] = { + .reg = 1, + .mask = WM8994_GP2_EINT, + }, + [WM8994_IRQ_GPIO(3)] = { + .reg = 1, + .mask = WM8994_GP3_EINT, + }, + [WM8994_IRQ_GPIO(4)] = { + .reg = 1, + .mask = WM8994_GP4_EINT, + }, + [WM8994_IRQ_GPIO(5)] = { + .reg = 1, + .mask = WM8994_GP5_EINT, + }, + [WM8994_IRQ_GPIO(6)] = { + .reg = 1, + .mask = WM8994_GP6_EINT, + }, + [WM8994_IRQ_GPIO(7)] = { + .reg = 1, + .mask = WM8994_GP7_EINT, + }, + [WM8994_IRQ_GPIO(8)] = { + .reg = 1, + .mask = WM8994_GP8_EINT, + }, + [WM8994_IRQ_GPIO(9)] = { + .reg = 1, + .mask = WM8994_GP8_EINT, + }, + [WM8994_IRQ_GPIO(10)] = { + .reg = 1, + .mask = WM8994_GP10_EINT, + }, + [WM8994_IRQ_GPIO(11)] = { + .reg = 1, + .mask = WM8994_GP11_EINT, + }, +}; + +static inline int irq_data_to_status_reg(struct wm8994_irq_data *irq_data) +{ + return WM8994_INTERRUPT_STATUS_1 - 1 + irq_data->reg; +} + +static inline int irq_data_to_mask_reg(struct wm8994_irq_data *irq_data) +{ + return WM8994_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg; +} + +static inline struct wm8994_irq_data *irq_to_wm8994_irq(struct wm8994 *wm8994, + int irq) +{ + return &wm8994_irqs[irq - wm8994->irq_base]; +} + +static void wm8994_irq_lock(unsigned int irq) +{ + struct wm8994 *wm8994 = get_irq_chip_data(irq); + + mutex_lock(&wm8994->irq_lock); +} + +static void wm8994_irq_sync_unlock(unsigned int irq) +{ + struct wm8994 *wm8994 = get_irq_chip_data(irq); + int i; + + for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) { + /* If there's been a change in the mask write it back + * to the hardware. */ + if (wm8994->irq_masks_cur[i] != wm8994->irq_masks_cache[i]) { + wm8994->irq_masks_cache[i] = wm8994->irq_masks_cur[i]; + wm8994_reg_write(wm8994, + WM8994_INTERRUPT_STATUS_1_MASK + i, + wm8994->irq_masks_cur[i]); + } + } + + mutex_unlock(&wm8994->irq_lock); +} + +static void wm8994_irq_unmask(unsigned int irq) +{ + struct wm8994 *wm8994 = get_irq_chip_data(irq); + struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, irq); + + wm8994->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; +} + +static void wm8994_irq_mask(unsigned int irq) +{ + struct wm8994 *wm8994 = get_irq_chip_data(irq); + struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, irq); + + wm8994->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; +} + +static struct irq_chip wm8994_irq_chip = { + .name = "wm8994", + .bus_lock = wm8994_irq_lock, + .bus_sync_unlock = wm8994_irq_sync_unlock, + .mask = wm8994_irq_mask, + .unmask = wm8994_irq_unmask, +}; + +/* The processing of the primary interrupt occurs in a thread so that + * we can interact with the device over I2C or SPI. */ +static irqreturn_t wm8994_irq_thread(int irq, void *data) +{ + struct wm8994 *wm8994 = data; + unsigned int i; + u16 status[WM8994_NUM_IRQ_REGS]; + int ret; + + ret = wm8994_bulk_read(wm8994, WM8994_INTERRUPT_STATUS_1, + WM8994_NUM_IRQ_REGS, status); + if (ret < 0) { + dev_err(wm8994->dev, "Failed to read interrupt status: %d\n", + ret); + return IRQ_NONE; + } + + /* Apply masking */ + for (i = 0; i < WM8994_NUM_IRQ_REGS; i++) + status[i] &= ~wm8994->irq_masks_cur[i]; + + /* Report */ + for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) { + if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask) + handle_nested_irq(wm8994->irq_base + i); + } + + /* Ack any unmasked IRQs */ + for (i = 0; i < ARRAY_SIZE(status); i++) { + if (status[i]) + wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1 + i, + status[i]); + } + + return IRQ_HANDLED; +} + +int wm8994_irq_init(struct wm8994 *wm8994) +{ + int i, cur_irq, ret; + + mutex_init(&wm8994->irq_lock); + + /* Mask the individual interrupt sources */ + for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) { + wm8994->irq_masks_cur[i] = 0xffff; + wm8994->irq_masks_cache[i] = 0xffff; + wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK + i, + 0xffff); + } + + if (!wm8994->irq) { + dev_warn(wm8994->dev, + "No interrupt specified, no interrupts\n"); + wm8994->irq_base = 0; + return 0; + } + + if (!wm8994->irq_base) { + dev_err(wm8994->dev, + "No interrupt base specified, no interrupts\n"); + return 0; + } + + /* Register them with genirq */ + for (cur_irq = wm8994->irq_base; + cur_irq < ARRAY_SIZE(wm8994_irqs) + wm8994->irq_base; + cur_irq++) { + set_irq_chip_data(cur_irq, wm8994); + set_irq_chip_and_handler(cur_irq, &wm8994_irq_chip, + handle_edge_irq); + set_irq_nested_thread(cur_irq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + set_irq_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(wm8994->irq, NULL, wm8994_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "wm8994", wm8994); + if (ret != 0) { + dev_err(wm8994->dev, "Failed to request IRQ %d: %d\n", + wm8994->irq, ret); + return ret; + } + + /* Enable top level interrupt if it was masked */ + wm8994_reg_write(wm8994, WM8994_INTERRUPT_CONTROL, 0); + + return 0; +} + +void wm8994_irq_exit(struct wm8994 *wm8994) +{ + if (wm8994->irq) + free_irq(wm8994->irq, wm8994); +} diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index f939ebc2507c..e306a8cd2f96 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -1,24 +1,20 @@ /* - Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and - Philip Edelbrock <phil@netroedge.com> - Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> - Copyright (C) 2003 IBM Corp. - Copyright (C) 2004 Jean Delvare <khali@linux-fr.org> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and + * Philip Edelbrock <phil@netroedge.com> + * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Jean Delvare <khali@linux-fr.org> + * + * 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 <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/misc/eeprom/eeprom_93cx6.c b/drivers/misc/eeprom/eeprom_93cx6.c index 15b1780025c8..7b33de95c4bf 100644 --- a/drivers/misc/eeprom/eeprom_93cx6.c +++ b/drivers/misc/eeprom/eeprom_93cx6.c @@ -1,27 +1,20 @@ /* - Copyright (C) 2004 - 2006 rt2x00 SourceForge Project - <http://rt2x00.serialmonkey.com> - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - Module: eeprom_93cx6 - Abstract: EEPROM reader routines for 93cx6 chipsets. - Supported chipsets: 93c46 & 93c66. + * Copyright (C) 2004 - 2006 rt2x00 SourceForge Project + * <http://rt2x00.serialmonkey.com> + * + * 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. + * + * Module: eeprom_93cx6 + * Abstract: EEPROM reader routines for 93cx6 chipsets. + * Supported chipsets: 93c46 & 93c66. */ #include <linux/kernel.h> diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 5a6b2bce8ad5..fe2909278507 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -1,30 +1,30 @@ /* - max6875.c - driver for MAX6874/MAX6875 - - Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> - - Based on eeprom.c - - The MAX6875 has a bank of registers and two banks of EEPROM. - Address ranges are defined as follows: - * 0x0000 - 0x0046 = configuration registers - * 0x8000 - 0x8046 = configuration EEPROM - * 0x8100 - 0x82FF = user EEPROM - - This driver makes the user EEPROM available for read. - - The registers & config EEPROM should be accessed via i2c-dev. - - The MAX6875 ignores the lowest address bit, so each chip responds to - two addresses - 0x50/0x51 and 0x52/0x53. - - Note that the MAX6875 uses i2c_smbus_write_byte_data() to set the read - address, so this driver is destructive if loaded for the wrong EEPROM chip. - - 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. -*/ + * max6875.c - driver for MAX6874/MAX6875 + * + * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> + * + * Based on eeprom.c + * + * The MAX6875 has a bank of registers and two banks of EEPROM. + * Address ranges are defined as follows: + * * 0x0000 - 0x0046 = configuration registers + * * 0x8000 - 0x8046 = configuration EEPROM + * * 0x8100 - 0x82FF = user EEPROM + * + * This driver makes the user EEPROM available for read. + * + * The registers & config EEPROM should be accessed via i2c-dev. + * + * The MAX6875 ignores the lowest address bit, so each chip responds to + * two addresses - 0x50/0x51 and 0x52/0x53. + * + * Note that the MAX6875 uses i2c_smbus_write_byte_data() to set the read + * address, so this driver is destructive if loaded for the wrong EEPROM chip. + * + * 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 <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/misc/vmware_balloon.c b/drivers/misc/vmware_balloon.c index e7161c4e3798..db9cd0240c6f 100644 --- a/drivers/misc/vmware_balloon.c +++ b/drivers/misc/vmware_balloon.c @@ -41,7 +41,7 @@ #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/seq_file.h> -#include <asm/vmware.h> +#include <asm/hypervisor.h> MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Memory Control (Balloon) Driver"); @@ -767,7 +767,7 @@ static int __init vmballoon_init(void) * Check if we are running on VMware's hypervisor and bail out * if we are not. */ - if (!vmware_platform()) + if (x86_hyper != &x86_hyper_vmware) return -ENODEV; vmballoon_wq = create_freezeable_workqueue("vmmemctl"); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 84c103a7ee13..ff115d920888 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -55,14 +55,16 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) host->cclk = host->mclk / (2 * (clk + 1)); } if (host->hw_designer == AMBA_VENDOR_ST) - clk |= MCI_FCEN; /* Bug fix in ST IP block */ + clk |= MCI_ST_FCEN; /* Bug fix in ST IP block */ clk |= MCI_CLK_ENABLE; /* This hasn't proven to be worthwhile */ /* clk |= MCI_CLK_PWRSAVE; */ } if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) - clk |= MCI_WIDE_BUS; + clk |= MCI_4BIT_BUS; + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) + clk |= MCI_ST_8BIT_BUS; writel(clk, host->base + MMCICLOCK); } @@ -629,7 +631,18 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc->ops = &mmci_ops; mmc->f_min = (host->mclk + 511) / 512; - mmc->f_max = min(host->mclk, fmax); + /* + * If the platform data supplies a maximum operating + * frequency, this takes precedence. Else, we fall back + * to using the module parameter, which has a (low) + * default value in case it is not specified. Either + * value must not exceed the clock rate into the block, + * of course. + */ + if (plat->f_max) + mmc->f_max = min(host->mclk, plat->f_max); + else + mmc->f_max = min(host->mclk, fmax); dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); #ifdef CONFIG_REGULATOR diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 1ceb9a90f59b..d77062e5e3af 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -25,9 +25,11 @@ #define MCI_CLK_ENABLE (1 << 8) #define MCI_CLK_PWRSAVE (1 << 9) #define MCI_CLK_BYPASS (1 << 10) -#define MCI_WIDE_BUS (1 << 11) +#define MCI_4BIT_BUS (1 << 11) +/* 8bit wide buses supported in ST Micro versions */ +#define MCI_ST_8BIT_BUS (1 << 12) /* HW flow control on the ST Micro version */ -#define MCI_FCEN (1 << 13) +#define MCI_ST_FCEN (1 << 13) #define MMCIARGUMENT 0x008 #define MMCICOMMAND 0x00c diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 0ed48959b590..e4f00e70a749 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -544,7 +544,7 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid) { struct pxamci_host *host = mmc_priv(devid); - mmc_detect_change(devid, host->pdata->detect_delay); + mmc_detect_change(devid, msecs_to_jiffies(host->pdata->detect_delay_ms)); return IRQ_HANDLED; } diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 689d6a79ffc0..87b2b8ff331e 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -52,7 +52,6 @@ static const int debug = 0; struct pcmciamtd_dev { struct pcmcia_device *p_dev; - dev_node_t node; /* device node */ caddr_t win_base; /* ioremapped address of PCMCIA window */ unsigned int win_size; /* size of window */ unsigned int offset; /* offset into card the window currently points at */ @@ -647,9 +646,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) pcmciamtd_release(link); return -ENODEV; } - snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index); info("mtd%d: %s", mtd->index, mtd->name); - link->dev_node = &dev->node; return 0; failed: diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c index ed5e9742be2c..a8f0512bad38 100644 --- a/drivers/net/a2065.c +++ b/drivers/net/a2065.c @@ -674,6 +674,7 @@ static struct zorro_device_id a2065_zorro_tbl[] __devinitdata = { { ZORRO_PROD_AMERISTAR_A2065 }, { 0 } }; +MODULE_DEVICE_TABLE(zorro, a2065_zorro_tbl); static struct zorro_driver a2065_driver = { .name = "a2065", diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c index fa1a2354f5f9..4b30a46486e2 100644 --- a/drivers/net/ariadne.c +++ b/drivers/net/ariadne.c @@ -145,6 +145,7 @@ static struct zorro_device_id ariadne_zorro_tbl[] __devinitdata = { { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE }, { 0 } }; +MODULE_DEVICE_TABLE(zorro, ariadne_zorro_tbl); static struct zorro_driver ariadne_driver = { .name = "ariadne", diff --git a/drivers/net/bnx2x_hsi.h b/drivers/net/bnx2x_hsi.h index 760069345b11..fd1f29e0317d 100644 --- a/drivers/net/bnx2x_hsi.h +++ b/drivers/net/bnx2x_hsi.h @@ -683,7 +683,7 @@ struct drv_func_mb { #define DRV_MSG_CODE_GET_MANUF_KEY 0x82000000 #define DRV_MSG_CODE_LOAD_L2B_PRAM 0x90000000 /* - * The optic module verification commands requris bootcode + * The optic module verification commands require bootcode * v5.0.6 or later */ #define DRV_MSG_CODE_VRFY_OPT_MDL 0xa0000000 diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index dbf81788bb40..d5d55c6a373f 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -2524,12 +2524,12 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) * excessive C-state transition latencies result in * dropped transactions. */ - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, - adapter->netdev->name, 55); + pm_qos_update_request( + adapter->netdev->pm_qos_req, 55); } else { - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, - adapter->netdev->name, - PM_QOS_DEFAULT_VALUE); + pm_qos_update_request( + adapter->netdev->pm_qos_req, + PM_QOS_DEFAULT_VALUE); } } @@ -2824,8 +2824,8 @@ int e1000e_up(struct e1000_adapter *adapter) /* DMA latency requirement to workaround early-receive/jumbo issue */ if (adapter->flags & FLAG_HAS_ERT) - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, - adapter->netdev->name, + adapter->netdev->pm_qos_req = + pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); /* hardware has been reset, we need to reload some things */ @@ -2887,9 +2887,11 @@ void e1000e_down(struct e1000_adapter *adapter) e1000_clean_tx_ring(adapter); e1000_clean_rx_ring(adapter); - if (adapter->flags & FLAG_HAS_ERT) - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, - adapter->netdev->name); + if (adapter->flags & FLAG_HAS_ERT) { + pm_qos_remove_request( + adapter->netdev->pm_qos_req); + adapter->netdev->pm_qos_req = NULL; + } /* * TODO: for power management, we could drop the link and diff --git a/drivers/net/hydra.c b/drivers/net/hydra.c index 24724b4ad709..07d8e5b634f3 100644 --- a/drivers/net/hydra.c +++ b/drivers/net/hydra.c @@ -71,6 +71,7 @@ static struct zorro_device_id hydra_zorro_tbl[] __devinitdata = { { ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET }, { 0 } }; +MODULE_DEVICE_TABLE(zorro, hydra_zorro_tbl); static struct zorro_driver hydra_driver = { .name = "hydra", diff --git a/drivers/net/igbvf/netdev.c b/drivers/net/igbvf/netdev.c index 1b1edad1eb5e..f16e981812a9 100644 --- a/drivers/net/igbvf/netdev.c +++ b/drivers/net/igbvf/netdev.c @@ -48,6 +48,7 @@ #define DRV_VERSION "1.0.0-k0" char igbvf_driver_name[] = "igbvf"; const char igbvf_driver_version[] = DRV_VERSION; +struct pm_qos_request_list *igbvf_driver_pm_qos_req; static const char igbvf_driver_string[] = "Intel(R) Virtual Function Network Driver"; static const char igbvf_copyright[] = "Copyright (c) 2009 Intel Corporation."; @@ -2899,7 +2900,7 @@ static int __init igbvf_init_module(void) printk(KERN_INFO "%s\n", igbvf_copyright); ret = pci_register_driver(&igbvf_driver); - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, igbvf_driver_name, + igbvf_driver_pm_qos_req = pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); return ret; @@ -2915,7 +2916,8 @@ module_init(igbvf_init_module); static void __exit igbvf_exit_module(void) { pci_unregister_driver(&igbvf_driver); - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, igbvf_driver_name); + pm_qos_remove_request(igbvf_driver_pm_qos_req); + igbvf_driver_pm_qos_req = NULL; } module_exit(igbvf_exit_module); diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index 757f87bb1db3..30b7cf70fbe6 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -93,7 +93,6 @@ earlier 3Com products. #include <pcmcia/cisreg.h> #include <pcmcia/ciscode.h> #include <pcmcia/ds.h> -#include <pcmcia/mem_op.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -200,7 +199,6 @@ enum Window4 { /* Window 4: Xcvr/media bits. */ struct el3_private { struct pcmcia_device *p_dev; - dev_node_t node; u16 advertising, partner; /* NWay media advertisement */ unsigned char phys; /* MII device address */ unsigned int autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */ @@ -283,8 +281,6 @@ static int tc574_probe(struct pcmcia_device *link) spin_lock_init(&lp->window_lock); link->io.NumPorts1 = 32; link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = &el3_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; @@ -311,8 +307,7 @@ static void tc574_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "3c574_detach()\n"); - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); tc574_release(link); @@ -353,7 +348,7 @@ static int tc574_config(struct pcmcia_device *link) if (i != 0) goto failed; - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, el3_interrupt); if (ret) goto failed; @@ -361,7 +356,7 @@ static int tc574_config(struct pcmcia_device *link) if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; ioaddr = dev->base_addr; @@ -446,17 +441,13 @@ static int tc574_config(struct pcmcia_device *link) } } - link->dev_node = &lp->node; SET_NETDEV_DEV(dev, &link->dev); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n"); - link->dev_node = NULL; goto failed; } - strcpy(lp->node.dev_name, dev->name); - printk(KERN_INFO "%s: %s at io %#3lx, irq %d, " "hw_addr %pM.\n", dev->name, cardname, dev->base_addr, dev->irq, diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 091e0b00043e..5ab589d3b385 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -106,7 +106,6 @@ enum RxFilter { struct el3_private { struct pcmcia_device *p_dev; - dev_node_t node; /* For transceiver monitoring */ struct timer_list media; u16 media_status; @@ -194,8 +193,7 @@ static int tc589_probe(struct pcmcia_device *link) spin_lock_init(&lp->lock); link->io.NumPorts1 = 16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = &el3_interrupt; + link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; @@ -223,8 +221,7 @@ static void tc589_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "3c589_detach\n"); - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); tc589_release(link); @@ -242,7 +239,6 @@ static void tc589_detach(struct pcmcia_device *link) static int tc589_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; - struct el3_private *lp = netdev_priv(dev); __be16 *phys_addr; int ret, i, j, multi = 0, fifo; unsigned int ioaddr; @@ -271,7 +267,7 @@ static int tc589_config(struct pcmcia_device *link) if (i != 0) goto failed; - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, el3_interrupt); if (ret) goto failed; @@ -279,7 +275,7 @@ static int tc589_config(struct pcmcia_device *link) if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; ioaddr = dev->base_addr; EL3WINDOW(0); @@ -313,17 +309,13 @@ static int tc589_config(struct pcmcia_device *link) else printk(KERN_ERR "3c589_cs: invalid if_port requested\n"); - link->dev_node = &lp->node; SET_NETDEV_DEV(dev, &link->dev); if (register_netdev(dev) != 0) { printk(KERN_ERR "3c589_cs: register_netdev() failed\n"); - link->dev_node = NULL; goto failed; } - strcpy(lp->node.dev_name, dev->name); - printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, " "hw_addr %pM\n", dev->name, (multi ? "562" : "589"), dev->base_addr, dev->irq, diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 9f3d593f14ed..59f6fa3c9ddc 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -113,7 +113,6 @@ static irqreturn_t ax_interrupt(int irq, void *dev_id); typedef struct axnet_dev_t { struct pcmcia_device *p_dev; - dev_node_t node; caddr_t base; struct timer_list watchdog; int stale, fast_poll; @@ -168,7 +167,6 @@ static int axnet_probe(struct pcmcia_device *link) info = PRIV(dev); info->p_dev = link; link->priv = dev; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -195,8 +193,7 @@ static void axnet_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link); - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); axnet_release(link); @@ -265,12 +262,9 @@ static int try_io_port(struct pcmcia_device *link) int j, ret; if (link->io.NumPorts1 == 32) { link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; - if (link->io.NumPorts2 > 0) { - /* for master/slave multifunction cards */ + /* for master/slave multifunction cards */ + if (link->io.NumPorts2 > 0) link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; - link->irq.Attributes = - IRQ_TYPE_DYNAMIC_SHARING; - } } else { /* This should be two 16-port windows */ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; @@ -336,8 +330,7 @@ static int axnet_config(struct pcmcia_device *link) if (ret != 0) goto failed; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) + if (!link->irq) goto failed; if (link->io.NumPorts2 == 8) { @@ -349,7 +342,7 @@ static int axnet_config(struct pcmcia_device *link) if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; if (!get_prom(link)) { @@ -397,17 +390,13 @@ static int axnet_config(struct pcmcia_device *link) } info->phy_id = (i < 32) ? i : -1; - link->dev_node = &info->node; SET_NETDEV_DEV(dev, &link->dev); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "axnet_cs: register_netdev() failed\n"); - link->dev_node = NULL; goto failed; } - strcpy(info->node.dev_name, dev->name); - printk(KERN_INFO "%s: Asix AX88%d90: io %#3lx, irq %d, " "hw_addr %pM\n", dev->name, ((info->flags & IS_AX88790) ? 7 : 1), diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c index 21d9c9d815d1..5643f94541bc 100644 --- a/drivers/net/pcmcia/com20020_cs.c +++ b/drivers/net/pcmcia/com20020_cs.c @@ -122,7 +122,6 @@ static void com20020_detach(struct pcmcia_device *p_dev); typedef struct com20020_dev_t { struct net_device *dev; - dev_node_t node; } com20020_dev_t; /*====================================================================== @@ -163,7 +162,6 @@ static int com20020_probe(struct pcmcia_device *p_dev) p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; p_dev->io.NumPorts1 = 16; p_dev->io.IOAddrLines = 16; - p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE; p_dev->conf.Attributes = CONF_ENABLE_IRQ; p_dev->conf.IntType = INT_MEMORY_AND_IO; @@ -196,18 +194,16 @@ static void com20020_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "com20020_detach\n"); - if (link->dev_node) { - dev_dbg(&link->dev, "unregister...\n"); + dev_dbg(&link->dev, "unregister...\n"); - unregister_netdev(dev); + unregister_netdev(dev); - /* - * this is necessary because we register our IRQ separately - * from card services. - */ - if (dev->irq) + /* + * this is necessary because we register our IRQ separately + * from card services. + */ + if (dev->irq) free_irq(dev->irq, dev); - } com20020_release(link); @@ -275,15 +271,14 @@ static int com20020_config(struct pcmcia_device *link) dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr); dev_dbg(&link->dev, "request IRQ %d\n", - link->irq.AssignedIRQ); - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) + link->irq); + if (!link->irq) { dev_dbg(&link->dev, "requestIRQ failed totally!\n"); goto failed; } - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; ret = pcmcia_request_configuration(link, &link->conf); if (ret) @@ -299,7 +294,6 @@ static int com20020_config(struct pcmcia_device *link) lp->card_name = "PCMCIA COM20020"; lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */ - link->dev_node = &info->node; SET_NETDEV_DEV(dev, &link->dev); i = com20020_found(dev, 0); /* calls register_netdev */ @@ -307,12 +301,9 @@ static int com20020_config(struct pcmcia_device *link) if (i != 0) { dev_printk(KERN_NOTICE, &link->dev, "com20020_cs: com20020_found() failed\n"); - link->dev_node = NULL; goto failed; } - strcpy(info->node.dev_name, dev->name); - dev_dbg(&link->dev,KERN_INFO "%s: port %#3lx, irq %d\n", dev->name, dev->base_addr, dev->irq); return 0; diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index b9dc80b9d04a..6580d78397d1 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -110,7 +110,6 @@ typedef enum { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN, */ typedef struct local_info_t { struct pcmcia_device *p_dev; - dev_node_t node; long open_time; uint tx_started:1; uint tx_queue; @@ -254,10 +253,6 @@ static int fmvj18x_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 5; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = fjn_interrupt; - /* General socket configuration */ link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -278,8 +273,7 @@ static void fmvj18x_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "fmvj18x_detach\n"); - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); fmvj18x_release(link); @@ -425,8 +419,6 @@ static int fmvj18x_config(struct pcmcia_device *link) } if (link->io.NumPorts2 != 0) { - link->irq.Attributes = - IRQ_TYPE_DYNAMIC_SHARING; ret = mfc_try_io_port(link); if (ret != 0) goto failed; } else if (cardtype == UNGERMANN) { @@ -437,14 +429,14 @@ static int fmvj18x_config(struct pcmcia_device *link) if (ret) goto failed; } - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, fjn_interrupt); if (ret) goto failed; ret = pcmcia_request_configuration(link, &link->conf); if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; if (link->io.BasePort2 != 0) { @@ -529,17 +521,13 @@ static int fmvj18x_config(struct pcmcia_device *link) } lp->cardtype = cardtype; - link->dev_node = &lp->node; SET_NETDEV_DEV(dev, &link->dev); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n"); - link->dev_node = NULL; goto failed; } - strcpy(lp->node.dev_name, dev->name); - /* print current configuration */ printk(KERN_INFO "%s: %s, sram %s, port %#3lx, irq %d, " "hw_addr %pM\n", diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c index 37f4a6fdc3ef..2e42d80f8cae 100644 --- a/drivers/net/pcmcia/ibmtr_cs.c +++ b/drivers/net/pcmcia/ibmtr_cs.c @@ -104,7 +104,6 @@ static void ibmtr_detach(struct pcmcia_device *p_dev); typedef struct ibmtr_dev_t { struct pcmcia_device *p_dev; struct net_device *dev; - dev_node_t node; window_handle_t sram_win_handle; struct tok_info *ti; } ibmtr_dev_t; @@ -156,8 +155,6 @@ static int __devinit ibmtr_attach(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 4; link->io.IOAddrLines = 16; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.Handler = ibmtr_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; @@ -192,8 +189,7 @@ static void ibmtr_detach(struct pcmcia_device *link) */ ti->sram_phys |= 1; - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); del_timer_sync(&(ti->tr_timer)); @@ -238,11 +234,11 @@ static int __devinit ibmtr_config(struct pcmcia_device *link) } dev->base_addr = link->io.BasePort1; - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_exclusive_irq(link, ibmtr_interrupt); if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; - ti->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; + ti->irq = link->irq; ti->global_int_enable=GLOBAL_INT_ENABLE+((dev->irq==9) ? 2 : dev->irq); /* Allocate the MMIO memory window */ @@ -291,18 +287,14 @@ static int __devinit ibmtr_config(struct pcmcia_device *link) Adapters Technical Reference" SC30-3585 for this info. */ ibmtr_hw_setup(dev, mmiobase); - link->dev_node = &info->node; SET_NETDEV_DEV(dev, &link->dev); i = ibmtr_probe_card(dev); if (i != 0) { printk(KERN_NOTICE "ibmtr_cs: register_netdev() failed\n"); - link->dev_node = NULL; goto failed; } - strcpy(info->node.dev_name, dev->name); - printk(KERN_INFO "%s: port %#3lx, irq %d, mmio %#5lx, sram %#5lx, hwaddr=%pM\n", dev->name, dev->base_addr, dev->irq, diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index c717b143f11a..d8a3b3cf246e 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -363,7 +363,6 @@ typedef struct _mace_statistics { typedef struct _mace_private { struct pcmcia_device *p_dev; - dev_node_t node; struct net_device_stats linux_stats; /* Linux statistics counters */ mace_statistics mace_stats; /* MACE chip statistics counters */ @@ -463,8 +462,6 @@ static int nmclan_probe(struct pcmcia_device *link) link->io.NumPorts1 = 32; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 5; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.Handler = mace_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; @@ -493,8 +490,7 @@ static void nmclan_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "nmclan_detach\n"); - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); nmclan_release(link); @@ -652,14 +648,14 @@ static int nmclan_config(struct pcmcia_device *link) ret = pcmcia_request_io(link, &link->io); if (ret) goto failed; - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_exclusive_irq(link, mace_interrupt); if (ret) goto failed; ret = pcmcia_request_configuration(link, &link->conf); if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; ioaddr = dev->base_addr; @@ -698,18 +694,14 @@ static int nmclan_config(struct pcmcia_device *link) else printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n"); - link->dev_node = &lp->node; SET_NETDEV_DEV(dev, &link->dev); i = register_netdev(dev); if (i != 0) { printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n"); - link->dev_node = NULL; goto failed; } - strcpy(lp->node.dev_name, dev->name); - printk(KERN_INFO "%s: nmclan: port %#3lx, irq %d, %s port," " hw_addr %pM\n", dev->name, dev->base_addr, dev->irq, if_names[dev->if_port], diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index 4c0368de1815..6f77a768ba88 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -208,7 +208,6 @@ static hw_info_t dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII }; typedef struct pcnet_dev_t { struct pcmcia_device *p_dev; - dev_node_t node; u_int flags; void __iomem *base; struct timer_list watchdog; @@ -264,7 +263,6 @@ static int pcnet_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = dev; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -288,8 +286,7 @@ static void pcnet_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "pcnet_detach\n"); - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); pcnet_release(link); @@ -488,8 +485,6 @@ static int try_io_port(struct pcmcia_device *link) if (link->io.NumPorts2 > 0) { /* for master/slave multifunction cards */ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; - link->irq.Attributes = - IRQ_TYPE_DYNAMIC_SHARING; } } else { /* This should be two 16-port windows */ @@ -559,8 +554,7 @@ static int pcnet_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) + if (!link->irq) goto failed; if (link->io.NumPorts2 == 8) { @@ -574,7 +568,7 @@ static int pcnet_config(struct pcmcia_device *link) ret = pcmcia_request_configuration(link, &link->conf); if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; if (info->flags & HAS_MISC_REG) { if ((if_port == 1) || (if_port == 2)) @@ -643,17 +637,13 @@ static int pcnet_config(struct pcmcia_device *link) if (info->flags & (IS_DL10019|IS_DL10022)) mii_phy_probe(dev); - link->dev_node = &info->node; SET_NETDEV_DEV(dev, &link->dev); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "pcnet_cs: register_netdev() failed\n"); - link->dev_node = NULL; goto failed; } - strcpy(info->node.dev_name, dev->name); - if (info->flags & (IS_DL10019|IS_DL10022)) { u_char id = inb(dev->base_addr + 0x1a); printk(KERN_INFO "%s: NE2000 (DL100%d rev %02x): ", diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index ccc553782a0d..59796e7d09c4 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -103,7 +103,6 @@ struct smc_private { u_short manfid; u_short cardid; - dev_node_t node; struct sk_buff *saved_skb; int packets_waiting; void __iomem *base; @@ -323,14 +322,11 @@ static int smc91c92_probe(struct pcmcia_device *link) return -ENOMEM; smc = netdev_priv(dev); smc->p_dev = link; - link->priv = dev; spin_lock_init(&smc->lock); link->io.NumPorts1 = 16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 4; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = &smc_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -363,8 +359,7 @@ static void smc91c92_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "smc91c92_detach\n"); - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); smc91c92_release(link); @@ -453,7 +448,6 @@ static int mhz_mfc_config(struct pcmcia_device *link) link->conf.Attributes |= CONF_ENABLE_SPKR; link->conf.Status = CCSR_AUDIO_ENA; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->io.IOAddrLines = 16; link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts2 = 8; @@ -652,7 +646,6 @@ static int osi_config(struct pcmcia_device *link) link->conf.Attributes |= CONF_ENABLE_SPKR; link->conf.Status = CCSR_AUDIO_ENA; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->io.NumPorts1 = 64; link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts2 = 8; @@ -877,7 +870,7 @@ static int smc91c92_config(struct pcmcia_device *link) if (i) goto config_failed; - i = pcmcia_request_irq(link, &link->irq); + i = pcmcia_request_irq(link, smc_interrupt); if (i) goto config_failed; i = pcmcia_request_configuration(link, &link->conf); @@ -887,7 +880,7 @@ static int smc91c92_config(struct pcmcia_device *link) if (smc->manfid == MANFID_MOTOROLA) mot_config(link); - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; if ((if_port >= 0) && (if_port <= 2)) dev->if_port = if_port; @@ -960,17 +953,13 @@ static int smc91c92_config(struct pcmcia_device *link) SMC_SELECT_BANK(0); } - link->dev_node = &smc->node; SET_NETDEV_DEV(dev, &link->dev); if (register_netdev(dev) != 0) { printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n"); - link->dev_node = NULL; goto config_undo; } - strcpy(smc->node.dev_name, dev->name); - printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, " "hw_addr %pM\n", dev->name, name, (rev & 0x0f), dev->base_addr, dev->irq, diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index 4d1802e457be..5e6b62ba8887 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -297,31 +297,9 @@ static void xirc2ps_detach(struct pcmcia_device *p_dev); static irqreturn_t xirc2ps_interrupt(int irq, void *dev_id); -/**************** - * A linked list of "instances" of the device. Each actual - * PCMCIA card corresponds to one device instance, and is described - * by one struct pcmcia_device structure (defined in ds.h). - * - * You may not want to use a linked list for this -- for example, the - * memory card driver uses an array of struct pcmcia_device pointers, where minor - * device numbers are used to derive the corresponding array index. - */ - -/**************** - * A driver needs to provide a dev_node_t structure for each device - * on a card. In some cases, there is only one device per card (for - * example, ethernet cards, modems). In other cases, there may be - * many actual or logical devices (SCSI adapters, memory cards with - * multiple partitions). The dev_node_t structures need to be kept - * in a linked list starting at the 'dev' field of a struct pcmcia_device - * structure. We allocate them in the card's private data structure, - * because they generally can't be allocated dynamically. - */ - typedef struct local_info_t { struct net_device *dev; struct pcmcia_device *p_dev; - dev_node_t node; int card_type; int probe_port; @@ -555,7 +533,6 @@ xirc2ps_probe(struct pcmcia_device *link) link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; - link->irq.Handler = xirc2ps_interrupt; /* Fill in card specific entries */ dev->netdev_ops = &netdev_ops; @@ -580,8 +557,7 @@ xirc2ps_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "detach\n"); - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); xirc2ps_release(link); @@ -841,7 +817,6 @@ xirc2ps_config(struct pcmcia_device * link) link->conf.Attributes |= CONF_ENABLE_SPKR; link->conf.Status |= CCSR_AUDIO_ENA; } - link->irq.Attributes |= IRQ_TYPE_DYNAMIC_SHARING; link->io.NumPorts2 = 8; link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; if (local->dingo) { @@ -866,7 +841,6 @@ xirc2ps_config(struct pcmcia_device * link) } printk(KNOT_XIRC "no ports available\n"); } else { - link->irq.Attributes |= IRQ_TYPE_DYNAMIC_SHARING; link->io.NumPorts1 = 16; for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) { link->io.BasePort1 = ioaddr; @@ -885,7 +859,7 @@ xirc2ps_config(struct pcmcia_device * link) * Now allocate an interrupt line. Note that this does not * actually assign a handler to the interrupt. */ - if ((err=pcmcia_request_irq(link, &link->irq))) + if ((err=pcmcia_request_irq(link, xirc2ps_interrupt))) goto config_error; /**************** @@ -982,23 +956,19 @@ xirc2ps_config(struct pcmcia_device * link) printk(KNOT_XIRC "invalid if_port requested\n"); /* we can now register the device with the net subsystem */ - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; if (local->dingo) do_reset(dev, 1); /* a kludge to make the cem56 work */ - link->dev_node = &local->node; SET_NETDEV_DEV(dev, &link->dev); if ((err=register_netdev(dev))) { printk(KNOT_XIRC "register_netdev() failed\n"); - link->dev_node = NULL; goto config_error; } - strcpy(local->node.dev_name, dev->name); - /* give some infos about the hardware */ printk(KERN_INFO "%s: %s: port %#3lx, irq %d, hwaddr %pM\n", dev->name, local->manf_str,(u_long)dev->base_addr, (int)dev->irq, diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c index f0be507e5324..369a8016b1ff 100644 --- a/drivers/net/ps3_gelic_wireless.c +++ b/drivers/net/ps3_gelic_wireless.c @@ -96,7 +96,7 @@ static inline int precise_ie(void) * post_eurus_cmd helpers */ struct eurus_cmd_arg_info { - int pre_arg; /* command requres arg1, arg2 at POST COMMAND */ + int pre_arg; /* command requires arg1, arg2 at POST COMMAND */ int post_arg; /* command requires arg1, arg2 at GET_RESULT */ }; diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index cbf520d38eac..ffbaa608e002 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -736,7 +736,7 @@ static void smsc911x_phy_adjust_link(struct net_device *dev) SMSC_TRACE(HW, "configuring for carrier OK"); if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) && (!pdata->using_extphy)) { - /* Restore orginal GPIO configuration */ + /* Restore original GPIO configuration */ pdata->gpio_setting = pdata->gpio_orig_setting; smsc911x_reg_write(pdata, GPIO_CFG, pdata->gpio_setting); @@ -750,7 +750,7 @@ static void smsc911x_phy_adjust_link(struct net_device *dev) if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) && (!pdata->using_extphy)) { /* Force 10/100 LED off, after saving - * orginal GPIO configuration */ + * original GPIO configuration */ pdata->gpio_orig_setting = pdata->gpio_setting; pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_; diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c index f6036fb42319..33bdc6a84e81 100644 --- a/drivers/net/wireless/airo_cs.c +++ b/drivers/net/wireless/airo_cs.c @@ -75,42 +75,7 @@ static void airo_release(struct pcmcia_device *link); static void airo_detach(struct pcmcia_device *p_dev); -/* - You'll also need to prototype all the functions that will actually - be used to talk to your device. See 'pcmem_cs' for a good example - of a fully self-sufficient driver; the other drivers rely more or - less on other parts of the kernel. -*/ - -/* - A linked list of "instances" of the aironet device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one struct pcmcia_device structure (defined in ds.h). - - You may not want to use a linked list for this -- for example, the - memory card driver uses an array of struct pcmcia_device pointers, - where minor device numbers are used to derive the corresponding - array index. -*/ - -/* - A driver needs to provide a dev_node_t structure for each device - on a card. In some cases, there is only one device per card (for - example, ethernet cards, modems). In other cases, there may be - many actual or logical devices (SCSI adapters, memory cards with - multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a struct pcmcia_device - structure. We allocate them in the card's private data structure, - because they generally shouldn't be allocated dynamically. - - In this case, we also provide a flag to indicate if a device is - "stopped" due to a power management event, or card ejection. The - device IO routines can use a flag like this to throttle IO to a - card that is not ready to accept it. -*/ - typedef struct local_info_t { - dev_node_t node; struct net_device *eth_dev; } local_info_t; @@ -132,10 +97,6 @@ static int airo_probe(struct pcmcia_device *p_dev) dev_dbg(&p_dev->dev, "airo_attach()\n"); - /* Interrupt setup */ - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - p_dev->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -212,9 +173,7 @@ static int airo_cs_config_check(struct pcmcia_device *p_dev, else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM)) p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; - /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -300,16 +259,8 @@ static int airo_config(struct pcmcia_device *link) if (ret) goto failed; - /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } + if (!link->irq) + goto failed; /* This actually configures the PCMCIA socket -- setting up @@ -320,26 +271,17 @@ static int airo_config(struct pcmcia_device *link) if (ret) goto failed; ((local_info_t *)link->priv)->eth_dev = - init_airo_card(link->irq.AssignedIRQ, + init_airo_card(link->irq, link->io.BasePort1, 1, &link->dev); if (!((local_info_t *)link->priv)->eth_dev) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev_node. - */ - strcpy(dev->node.dev_name, ((local_info_t *)link->priv)->eth_dev->name); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x: ", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x: ", + link->conf.ConfigIndex); if (link->conf.Vpp) printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index 32407911842f..c2746fc7f2be 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -85,41 +85,7 @@ static void atmel_release(struct pcmcia_device *link); static void atmel_detach(struct pcmcia_device *p_dev); -/* - You'll also need to prototype all the functions that will actually - be used to talk to your device. See 'pcmem_cs' for a good example - of a fully self-sufficient driver; the other drivers rely more or - less on other parts of the kernel. -*/ - -/* - A linked list of "instances" of the atmelnet device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one struct pcmcia_device structure (defined in ds.h). - - You may not want to use a linked list for this -- for example, the - memory card driver uses an array of struct pcmcia_device pointers, where minor - device numbers are used to derive the corresponding array index. -*/ - -/* - A driver needs to provide a dev_node_t structure for each device - on a card. In some cases, there is only one device per card (for - example, ethernet cards, modems). In other cases, there may be - many actual or logical devices (SCSI adapters, memory cards with - multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a struct pcmcia_device - structure. We allocate them in the card's private data structure, - because they generally shouldn't be allocated dynamically. - - In this case, we also provide a flag to indicate if a device is - "stopped" due to a power management event, or card ejection. The - device IO routines can use a flag like this to throttle IO to a - card that is not ready to accept it. -*/ - typedef struct local_info_t { - dev_node_t node; struct net_device *eth_dev; } local_info_t; @@ -141,10 +107,6 @@ static int atmel_probe(struct pcmcia_device *p_dev) dev_dbg(&p_dev->dev, "atmel_attach()\n"); - /* Interrupt setup */ - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - p_dev->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -226,9 +188,7 @@ static int atmel_config_check(struct pcmcia_device *p_dev, else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM)) p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; - /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -278,15 +238,9 @@ static int atmel_config(struct pcmcia_device *link) if (pcmcia_loop_config(link, atmel_config_check, NULL)) goto failed; - /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; + if (!link->irq) { + dev_err(&link->dev, "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config."); + goto failed; } /* @@ -298,14 +252,8 @@ static int atmel_config(struct pcmcia_device *link) if (ret) goto failed; - if (link->irq.AssignedIRQ == 0) { - printk(KERN_ALERT - "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config."); - goto failed; - } - ((local_info_t*)link->priv)->eth_dev = - init_atmel_card(link->irq.AssignedIRQ, + init_atmel_card(link->irq, link->io.BasePort1, did ? did->driver_info : ATMEL_FW_TYPE_NONE, &link->dev, @@ -315,14 +263,6 @@ static int atmel_config(struct pcmcia_device *link) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev_node. - */ - strcpy(dev->node.dev_name, ((local_info_t*)link->priv)->eth_dev->name ); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - return 0; failed: diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c index 609e7051e018..0e99b634267c 100644 --- a/drivers/net/wireless/b43/pcmcia.c +++ b/drivers/net/wireless/b43/pcmcia.c @@ -98,10 +98,7 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) if (res != 0) goto err_disable; - dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - dev->irq.Handler = NULL; /* The handler is registered later. */ - res = pcmcia_request_irq(dev, &dev->irq); - if (res != 0) + if (!dev->irq) goto err_disable; res = pcmcia_request_configuration(dev, &dev->conf); diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index a36501dbbe02..db72461c486b 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -39,7 +39,6 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); /* struct local_info::hw_priv */ struct hostap_cs_priv { - dev_node_t node; struct pcmcia_device *link; int sandisk_connectplus; }; @@ -556,15 +555,7 @@ static int prism2_config_check(struct pcmcia_device *p_dev, p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; - else if (!(p_dev->conf.Attributes & CONF_ENABLE_IRQ)) { - /* At least Compaq WL200 does not have IRQInfo1 set, - * but it does not work without interrupts.. */ - printk(KERN_WARNING "Config has no IRQ info, but trying to " - "enable IRQ anyway..\n"); - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; - } + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d " @@ -633,21 +624,10 @@ static int prism2_config(struct pcmcia_device *link) local = iface->local; local->hw_priv = hw_priv; hw_priv->link = link; - strcpy(hw_priv->node.dev_name, dev->name); - link->dev_node = &hw_priv->node; - /* - * Allocate an interrupt line. Note that this does not assign a - * handler to the interrupt, unless the 'Handler' member of the - * irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = prism2_interrupt; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } + ret = pcmcia_request_irq(link, prism2_interrupt); + if (ret) + goto failed; /* * This actually configures the PCMCIA socket -- setting up @@ -658,7 +638,7 @@ static int prism2_config(struct pcmcia_device *link) if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; /* Finally, report what we've done */ @@ -668,7 +648,7 @@ static int prism2_config(struct pcmcia_device *link) printk(", Vpp %d.%d", link->conf.Vpp / 10, link->conf.Vpp % 10); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); @@ -682,11 +662,9 @@ static int prism2_config(struct pcmcia_device *link) sandisk_enable_wireless(dev); ret = prism2_hw_config(dev, 1); - if (!ret) { + if (!ret) ret = hostap_hw_ready(dev); - if (ret == 0 && local->ddev) - strcpy(hw_priv->node.dev_name, local->ddev->name); - } + return ret; failed: diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 9b72c45a7748..2b05fe5e994c 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -174,6 +174,8 @@ that only one external action is invoked at a time. #define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" +struct pm_qos_request_list *ipw2100_pm_qos_req; + /* Debugging stuff */ #ifdef CONFIG_IPW2100_DEBUG #define IPW2100_RX_DEBUG /* Reception debugging */ @@ -1739,7 +1741,7 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) /* the ipw2100 hardware really doesn't want power management delays * longer than 175usec */ - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100", 175); + pm_qos_update_request(ipw2100_pm_qos_req, 175); /* If the interrupt is enabled, turn it off... */ spin_lock_irqsave(&priv->low_lock, flags); @@ -1887,8 +1889,7 @@ static void ipw2100_down(struct ipw2100_priv *priv) ipw2100_disable_interrupts(priv); spin_unlock_irqrestore(&priv->low_lock, flags); - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100", - PM_QOS_DEFAULT_VALUE); + pm_qos_update_request(ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); /* We have to signal any supplicant if we are disassociating */ if (associated) @@ -6669,7 +6670,7 @@ static int __init ipw2100_init(void) if (ret) goto out; - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100", + ipw2100_pm_qos_req = pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); #ifdef CONFIG_IPW2100_DEBUG ipw2100_debug_level = debug; @@ -6692,7 +6693,7 @@ static void __exit ipw2100_exit(void) &driver_attr_debug_level); #endif pci_unregister_driver(&ipw2100_pci_driver); - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100"); + pm_qos_remove_request(ipw2100_pm_qos_req); } module_init(ipw2100_init); diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 6d55439a7b97..08e4e3908003 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -777,7 +777,7 @@ static void if_cs_release(struct pcmcia_device *p_dev) lbs_deb_enter(LBS_DEB_CS); - free_irq(p_dev->irq.AssignedIRQ, card); + free_irq(p_dev->irq, card); pcmcia_disable_device(p_dev); if (card->iobase) ioport_unmap(card->iobase); @@ -807,8 +807,7 @@ static int if_cs_ioprobe(struct pcmcia_device *p_dev, p_dev->io.NumPorts1 = cfg->io.win[0].len; /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ if (cfg->io.nwin != 1) { @@ -837,9 +836,6 @@ static int if_cs_probe(struct pcmcia_device *p_dev) card->p_dev = p_dev; p_dev->priv = card; - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - p_dev->irq.Handler = NULL; - p_dev->conf.Attributes = 0; p_dev->conf.IntType = INT_MEMORY_AND_IO; @@ -854,13 +850,8 @@ static int if_cs_probe(struct pcmcia_device *p_dev) * a handler to the interrupt, unless the 'Handler' member of * the irq structure is initialized. */ - if (p_dev->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(p_dev, &p_dev->irq); - if (ret) { - lbs_pr_err("error in pcmcia_request_irq\n"); - goto out1; - } - } + if (!p_dev->irq) + goto out1; /* Initialize io access */ card->iobase = ioport_map(p_dev->io.BasePort1, p_dev->io.NumPorts1); @@ -883,7 +874,7 @@ static int if_cs_probe(struct pcmcia_device *p_dev) /* Finally, report what we've done */ lbs_deb_cs("irq %d, io 0x%04x-0x%04x\n", - p_dev->irq.AssignedIRQ, p_dev->io.BasePort1, + p_dev->irq, p_dev->io.BasePort1, p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1); /* @@ -940,7 +931,7 @@ static int if_cs_probe(struct pcmcia_device *p_dev) priv->fw_ready = 1; /* Now actually get the IRQ */ - ret = request_irq(p_dev->irq.AssignedIRQ, if_cs_interrupt, + ret = request_irq(p_dev->irq, if_cs_interrupt, IRQF_SHARED, DRV_NAME, card); if (ret) { lbs_pr_err("error in request_irq\n"); diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index 1d4ada188eda..03056ab73032 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -50,7 +50,6 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket * struct orinoco_private */ struct orinoco_pccard { struct pcmcia_device *p_dev; - dev_node_t node; /* Used to handle hard reset */ /* yuck, we need this hack to work around the insanity of the @@ -119,10 +118,6 @@ orinoco_cs_probe(struct pcmcia_device *link) card->p_dev = link; link->priv = priv; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = orinoco_interrupt; - /* General socket configuration defaults can go here. In this * client, we assume very little, and rely on the CIS for * almost everything. In most clients, many details (i.e., @@ -144,8 +139,7 @@ static void orinoco_cs_detach(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; - if (link->dev_node) - orinoco_if_del(priv); + orinoco_if_del(priv); orinoco_cs_release(link); @@ -230,7 +224,6 @@ static int orinoco_cs_config(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; - struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; int ret; void __iomem *mem; @@ -258,12 +251,7 @@ orinoco_cs_config(struct pcmcia_device *link) goto failed; } - /* - * Allocate an interrupt line. Note that this does not assign - * a handler to the interrupt, unless the 'Handler' member of - * the irq structure is initialized. - */ - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, orinoco_interrupt); if (ret) goto failed; @@ -285,9 +273,6 @@ orinoco_cs_config(struct pcmcia_device *link) if (ret) goto failed; - /* Ok, we have the configuration, prepare to register the netdev */ - card->node.major = card->node.minor = 0; - /* Initialise the main driver */ if (orinoco_init(priv) != 0) { printk(KERN_ERR PFX "orinoco_init() failed\n"); @@ -296,17 +281,11 @@ orinoco_cs_config(struct pcmcia_device *link) /* Register an interface with the stack */ if (orinoco_if_add(priv, link->io.BasePort1, - link->irq.AssignedIRQ) != 0) { + link->irq) != 0) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } - /* At this point, the dev_node_t structure(s) needs to be - * initialized and arranged in a linked list at link->dev_node. */ - strcpy(card->node.dev_name, priv->ndev->name); - link->dev_node = &card->node; /* link->dev_node being non-NULL is also - * used to indicate that the - * net_device has been registered */ return 0; failed: diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c index 59bda240fdc2..41b9ce425855 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -57,7 +57,6 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket * struct orinoco_private */ struct orinoco_pccard { struct pcmcia_device *p_dev; - dev_node_t node; }; /********************************************************************/ @@ -193,10 +192,6 @@ spectrum_cs_probe(struct pcmcia_device *link) card->p_dev = link; link->priv = priv; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = orinoco_interrupt; - /* General socket configuration defaults can go here. In this * client, we assume very little, and rely on the CIS for * almost everything. In most clients, many details (i.e., @@ -218,8 +213,7 @@ static void spectrum_cs_detach(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; - if (link->dev_node) - orinoco_if_del(priv); + orinoco_if_del(priv); spectrum_cs_release(link); @@ -304,7 +298,6 @@ static int spectrum_cs_config(struct pcmcia_device *link) { struct orinoco_private *priv = link->priv; - struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; int ret; void __iomem *mem; @@ -332,12 +325,7 @@ spectrum_cs_config(struct pcmcia_device *link) goto failed; } - /* - * Allocate an interrupt line. Note that this does not assign - * a handler to the interrupt, unless the 'Handler' member of - * the irq structure is initialized. - */ - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, orinoco_interrupt); if (ret) goto failed; @@ -359,9 +347,6 @@ spectrum_cs_config(struct pcmcia_device *link) if (ret) goto failed; - /* Ok, we have the configuration, prepare to register the netdev */ - card->node.major = card->node.minor = 0; - /* Reset card */ if (spectrum_cs_hard_reset(priv) != 0) goto failed; @@ -374,17 +359,11 @@ spectrum_cs_config(struct pcmcia_device *link) /* Register an interface with the stack */ if (orinoco_if_add(priv, link->io.BasePort1, - link->irq.AssignedIRQ) != 0) { + link->irq) != 0) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } - /* At this point, the dev_node_t structure(s) needs to be - * initialized and arranged in a linked list at link->dev_node. */ - strcpy(card->node.dev_name, priv->ndev->name); - link->dev_node = &card->node; /* link->dev_node being non-NULL is also - * used to indicate that the - * net_device has been registered */ return 0; failed: diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 11865ea21875..f7d2a34ca531 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -51,7 +51,6 @@ #include <pcmcia/cistpl.h> #include <pcmcia/cisreg.h> #include <pcmcia/ds.h> -#include <pcmcia/mem_op.h> #include <linux/wireless.h> #include <net/iw_handler.h> @@ -321,10 +320,6 @@ static int ray_probe(struct pcmcia_device *p_dev) p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; p_dev->io.IOAddrLines = 5; - /* Interrupt setup. For PCMCIA, driver takes what's given */ - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - p_dev->irq.Handler = &ray_interrupt; - /* General socket configuration */ p_dev->conf.Attributes = CONF_ENABLE_IRQ; p_dev->conf.IntType = INT_MEMORY_AND_IO; @@ -383,8 +378,7 @@ static void ray_detach(struct pcmcia_device *link) del_timer(&local->timer); if (link->priv) { - if (link->dev_node) - unregister_netdev(dev); + unregister_netdev(dev); free_netdev(dev); } dev_dbg(&link->dev, "ray_cs ray_detach ending\n"); @@ -417,10 +411,10 @@ static int ray_config(struct pcmcia_device *link) /* Now allocate an interrupt line. Note that this does not actually assign a handler to the interrupt. */ - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, ray_interrupt); if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping. @@ -493,9 +487,6 @@ static int ray_config(struct pcmcia_device *link) return i; } - strcpy(local->node.dev_name, dev->name); - link->dev_node = &local->node; - printk(KERN_INFO "%s: RayLink, irq %d, hw_addr %pM\n", dev->name, dev->irq, dev->dev_addr); diff --git a/drivers/net/wireless/ray_cs.h b/drivers/net/wireless/ray_cs.h index 1e23b7f4cca7..9f01ddb19748 100644 --- a/drivers/net/wireless/ray_cs.h +++ b/drivers/net/wireless/ray_cs.h @@ -25,7 +25,6 @@ struct beacon_rx { typedef struct ray_dev_t { int card_status; int authentication_state; - dev_node_t node; window_handle_t amem_handle; /* handle to window for attribute memory */ window_handle_t rmem_handle; /* handle to window for rx buffer on card */ void __iomem *sram; /* pointer to beginning of shared RAM */ diff --git a/drivers/net/wireless/wl3501.h b/drivers/net/wireless/wl3501.h index 8bce1a550a22..8816e371fd0e 100644 --- a/drivers/net/wireless/wl3501.h +++ b/drivers/net/wireless/wl3501.h @@ -610,7 +610,6 @@ struct wl3501_card { struct iw_statistics wstats; struct iw_spy_data spy_data; struct iw_public_data wireless_data; - struct dev_node_t node; struct pcmcia_device *p_dev; }; #endif diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 7b9621de239f..5e5d24c1ce2b 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1451,6 +1451,8 @@ static void wl3501_detach(struct pcmcia_device *link) netif_device_detach(dev); wl3501_release(link); + unregister_netdev(dev); + if (link->priv) free_netdev(link->priv); @@ -1897,10 +1899,6 @@ static int wl3501_probe(struct pcmcia_device *p_dev) p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; p_dev->io.IOAddrLines = 5; - /* Interrupt setup */ - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - p_dev->irq.Handler = wl3501_interrupt; - /* General socket configuration */ p_dev->conf.Attributes = CONF_ENABLE_IRQ; p_dev->conf.IntType = INT_MEMORY_AND_IO; @@ -1961,7 +1959,7 @@ static int wl3501_config(struct pcmcia_device *link) /* Now allocate an interrupt line. Note that this does not actually * assign a handler to the interrupt. */ - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, wl3501_interrupt); if (ret) goto failed; @@ -1972,7 +1970,7 @@ static int wl3501_config(struct pcmcia_device *link) if (ret) goto failed; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; SET_NETDEV_DEV(dev, &link->dev); if (register_netdev(dev)) { @@ -1981,20 +1979,15 @@ static int wl3501_config(struct pcmcia_device *link) } this = netdev_priv(dev); - /* - * At this point, the dev_node_t structure(s) should be initialized and - * arranged in a linked list at link->dev_node. - */ - link->dev_node = &this->node; this->base_addr = dev->base_addr; if (!wl3501_get_flash_mac_addr(this)) { printk(KERN_WARNING "%s: Cant read MAC addr in flash ROM?\n", dev->name); + unregister_netdev(dev); goto failed; } - strcpy(this->node.dev_name, dev->name); for (i = 0; i < 6; i++) dev->dev_addr[i] = ((char *)&this->mac_addr)[i]; @@ -2038,12 +2031,6 @@ failed: */ static void wl3501_release(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - - /* Unlink the device chain */ - if (link->dev_node) - unregister_netdev(dev); - pcmcia_disable_device(link); } diff --git a/drivers/net/zorro8390.c b/drivers/net/zorro8390.c index 81c753a617ab..9548cbb5012a 100644 --- a/drivers/net/zorro8390.c +++ b/drivers/net/zorro8390.c @@ -102,6 +102,7 @@ static struct zorro_device_id zorro8390_zorro_tbl[] __devinitdata = { { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, }, { 0 } }; +MODULE_DEVICE_TABLE(zorro, zorro8390_zorro_tbl); static struct zorro_driver zorro8390_driver = { .name = "zorro8390", diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 166b67ea622f..219f79e2210a 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -30,23 +30,7 @@ #define OP_BUFFER_FLAGS 0 -/* - * Read and write access is using spin locking. Thus, writing to the - * buffer by NMI handler (x86) could occur also during critical - * sections when reading the buffer. To avoid this, there are 2 - * buffers for independent read and write access. Read access is in - * process context only, write access only in the NMI handler. If the - * read buffer runs empty, both buffers are swapped atomically. There - * is potentially a small window during swapping where the buffers are - * disabled and samples could be lost. - * - * Using 2 buffers is a little bit overhead, but the solution is clear - * and does not require changes in the ring buffer implementation. It - * can be changed to a single buffer solution when the ring buffer - * access is implemented as non-locking atomic code. - */ -static struct ring_buffer *op_ring_buffer_read; -static struct ring_buffer *op_ring_buffer_write; +static struct ring_buffer *op_ring_buffer; DEFINE_PER_CPU(struct oprofile_cpu_buffer, op_cpu_buffer); static void wq_sync_buffer(struct work_struct *work); @@ -68,12 +52,9 @@ void oprofile_cpu_buffer_inc_smpl_lost(void) void free_cpu_buffers(void) { - if (op_ring_buffer_read) - ring_buffer_free(op_ring_buffer_read); - op_ring_buffer_read = NULL; - if (op_ring_buffer_write) - ring_buffer_free(op_ring_buffer_write); - op_ring_buffer_write = NULL; + if (op_ring_buffer) + ring_buffer_free(op_ring_buffer); + op_ring_buffer = NULL; } #define RB_EVENT_HDR_SIZE 4 @@ -86,11 +67,8 @@ int alloc_cpu_buffers(void) unsigned long byte_size = buffer_size * (sizeof(struct op_sample) + RB_EVENT_HDR_SIZE); - op_ring_buffer_read = ring_buffer_alloc(byte_size, OP_BUFFER_FLAGS); - if (!op_ring_buffer_read) - goto fail; - op_ring_buffer_write = ring_buffer_alloc(byte_size, OP_BUFFER_FLAGS); - if (!op_ring_buffer_write) + op_ring_buffer = ring_buffer_alloc(byte_size, OP_BUFFER_FLAGS); + if (!op_ring_buffer) goto fail; for_each_possible_cpu(i) { @@ -162,16 +140,11 @@ struct op_sample *op_cpu_buffer_write_reserve(struct op_entry *entry, unsigned long size) { entry->event = ring_buffer_lock_reserve - (op_ring_buffer_write, sizeof(struct op_sample) + + (op_ring_buffer, sizeof(struct op_sample) + size * sizeof(entry->sample->data[0])); - if (entry->event) - entry->sample = ring_buffer_event_data(entry->event); - else - entry->sample = NULL; - - if (!entry->sample) + if (!entry->event) return NULL; - + entry->sample = ring_buffer_event_data(entry->event); entry->size = size; entry->data = entry->sample->data; @@ -180,25 +153,16 @@ struct op_sample int op_cpu_buffer_write_commit(struct op_entry *entry) { - return ring_buffer_unlock_commit(op_ring_buffer_write, entry->event); + return ring_buffer_unlock_commit(op_ring_buffer, entry->event); } struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu) { struct ring_buffer_event *e; - e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL); - if (e) - goto event; - if (ring_buffer_swap_cpu(op_ring_buffer_read, - op_ring_buffer_write, - cpu)) + e = ring_buffer_consume(op_ring_buffer, cpu, NULL, NULL); + if (!e) return NULL; - e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL); - if (e) - goto event; - return NULL; -event: entry->event = e; entry->sample = ring_buffer_event_data(e); entry->size = (ring_buffer_event_length(e) - sizeof(struct op_sample)) @@ -209,8 +173,7 @@ event: unsigned long op_cpu_buffer_entries(int cpu) { - return ring_buffer_entries_cpu(op_ring_buffer_read, cpu) - + ring_buffer_entries_cpu(op_ring_buffer_write, cpu); + return ring_buffer_entries_cpu(op_ring_buffer, cpu); } static int @@ -356,8 +319,16 @@ void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) { - int is_kernel = !user_mode(regs); - unsigned long pc = profile_pc(regs); + int is_kernel; + unsigned long pc; + + if (likely(regs)) { + is_kernel = !user_mode(regs); + pc = profile_pc(regs); + } else { + is_kernel = 0; /* This value will not be used */ + pc = ESCAPE_CODE; /* as this causes an early return. */ + } __oprofile_add_ext_sample(pc, regs, event, is_kernel); } diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index dc8a0428260d..b336cd9ee7a1 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -253,22 +253,26 @@ static int __init oprofile_init(void) int err; err = oprofile_arch_init(&oprofile_ops); - if (err < 0 || timer) { printk(KERN_INFO "oprofile: using timer interrupt.\n"); - oprofile_timer_init(&oprofile_ops); + err = oprofile_timer_init(&oprofile_ops); + if (err) + goto out_arch; } - err = oprofilefs_register(); if (err) - oprofile_arch_exit(); + goto out_arch; + return 0; +out_arch: + oprofile_arch_exit(); return err; } static void __exit oprofile_exit(void) { + oprofile_timer_exit(); oprofilefs_unregister(); oprofile_arch_exit(); } diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index cb92f5c98c1a..47e12cb4ee8b 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h @@ -34,7 +34,8 @@ struct super_block; struct dentry; void oprofile_create_files(struct super_block *sb, struct dentry *root); -void oprofile_timer_init(struct oprofile_operations *ops); +int oprofile_timer_init(struct oprofile_operations *ops); +void oprofile_timer_exit(void); int oprofile_set_backtrace(unsigned long depth); int oprofile_set_timeout(unsigned long time); diff --git a/drivers/oprofile/timer_int.c b/drivers/oprofile/timer_int.c index 333f915568c7..dc0ae4d14dff 100644 --- a/drivers/oprofile/timer_int.c +++ b/drivers/oprofile/timer_int.c @@ -13,34 +13,94 @@ #include <linux/oprofile.h> #include <linux/profile.h> #include <linux/init.h> +#include <linux/cpu.h> +#include <linux/hrtimer.h> +#include <asm/irq_regs.h> #include <asm/ptrace.h> #include "oprof.h" -static int timer_notify(struct pt_regs *regs) +static DEFINE_PER_CPU(struct hrtimer, oprofile_hrtimer); + +static enum hrtimer_restart oprofile_hrtimer_notify(struct hrtimer *hrtimer) +{ + oprofile_add_sample(get_irq_regs(), 0); + hrtimer_forward_now(hrtimer, ns_to_ktime(TICK_NSEC)); + return HRTIMER_RESTART; +} + +static void __oprofile_hrtimer_start(void *unused) +{ + struct hrtimer *hrtimer = &__get_cpu_var(oprofile_hrtimer); + + hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer->function = oprofile_hrtimer_notify; + + hrtimer_start(hrtimer, ns_to_ktime(TICK_NSEC), + HRTIMER_MODE_REL_PINNED); +} + +static int oprofile_hrtimer_start(void) { - oprofile_add_sample(regs, 0); + on_each_cpu(__oprofile_hrtimer_start, NULL, 1); return 0; } -static int timer_start(void) +static void __oprofile_hrtimer_stop(int cpu) { - return register_timer_hook(timer_notify); + struct hrtimer *hrtimer = &per_cpu(oprofile_hrtimer, cpu); + + hrtimer_cancel(hrtimer); } +static void oprofile_hrtimer_stop(void) +{ + int cpu; + + for_each_online_cpu(cpu) + __oprofile_hrtimer_stop(cpu); +} -static void timer_stop(void) +static int __cpuinit oprofile_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) { - unregister_timer_hook(timer_notify); + long cpu = (long) hcpu; + + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + smp_call_function_single(cpu, __oprofile_hrtimer_start, + NULL, 1); + break; + case CPU_DEAD: + case CPU_DEAD_FROZEN: + __oprofile_hrtimer_stop(cpu); + break; + } + return NOTIFY_OK; } +static struct notifier_block __refdata oprofile_cpu_notifier = { + .notifier_call = oprofile_cpu_notify, +}; -void __init oprofile_timer_init(struct oprofile_operations *ops) +int __init oprofile_timer_init(struct oprofile_operations *ops) { + int rc; + + rc = register_hotcpu_notifier(&oprofile_cpu_notifier); + if (rc) + return rc; ops->create_files = NULL; ops->setup = NULL; ops->shutdown = NULL; - ops->start = timer_start; - ops->stop = timer_stop; + ops->start = oprofile_hrtimer_start; + ops->stop = oprofile_hrtimer_stop; ops->cpu_type = "timer"; + return 0; +} + +void __exit oprofile_timer_exit(void) +{ + unregister_hotcpu_notifier(&oprofile_cpu_notifier); } diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c index 7dd370fa3439..fd8cfe95f0a3 100644 --- a/drivers/parport/parport_cs.c +++ b/drivers/parport/parport_cs.c @@ -75,7 +75,6 @@ INT_MODULE_PARM(epp_mode, 1); typedef struct parport_info_t { struct pcmcia_device *p_dev; int ndev; - dev_node_t node; struct parport *port; } parport_info_t; @@ -105,7 +104,6 @@ static int parport_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -174,20 +172,19 @@ static int parport_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) + if (!link->irq) goto failed; ret = pcmcia_request_configuration(link, &link->conf); if (ret) goto failed; p = parport_pc_probe_port(link->io.BasePort1, link->io.BasePort2, - link->irq.AssignedIRQ, PARPORT_DMA_NONE, + link->irq, PARPORT_DMA_NONE, &link->dev, IRQF_SHARED); if (p == NULL) { printk(KERN_NOTICE "parport_cs: parport_pc_probe_port() at " "0x%3x, irq %u failed\n", link->io.BasePort1, - link->irq.AssignedIRQ); + link->irq); goto failed; } @@ -195,11 +192,7 @@ static int parport_config(struct pcmcia_device *link) if (epp_mode) p->modes |= PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP; info->ndev = 1; - info->node.major = LP_MAJOR; - info->node.minor = p->number; info->port = p; - strcpy(info->node.dev_name, p->name); - link->dev_node = &info->node; return 0; diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index f184d1d2ecbe..6644337d63d6 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -848,7 +848,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_disable_device; } - /* Check for the proper subsytem ID's + /* Check for the proper subsystem ID's * Intel uses a different SSID programming model than Compaq. * For Intel, each SSID bit identifies a PHP capability. * Also Intel HPC's may have RID=0. diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 417312528ddf..371dc564e2e4 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3626,14 +3626,15 @@ static void intel_iommu_detach_device(struct iommu_domain *domain, domain_remove_one_dev_info(dmar_domain, pdev); } -static int intel_iommu_map_range(struct iommu_domain *domain, - unsigned long iova, phys_addr_t hpa, - size_t size, int iommu_prot) +static int intel_iommu_map(struct iommu_domain *domain, + unsigned long iova, phys_addr_t hpa, + int gfp_order, int iommu_prot) { struct dmar_domain *dmar_domain = domain->priv; u64 max_addr; int addr_width; int prot = 0; + size_t size; int ret; if (iommu_prot & IOMMU_READ) @@ -3643,6 +3644,7 @@ static int intel_iommu_map_range(struct iommu_domain *domain, if ((iommu_prot & IOMMU_CACHE) && dmar_domain->iommu_snooping) prot |= DMA_PTE_SNP; + size = PAGE_SIZE << gfp_order; max_addr = iova + size; if (dmar_domain->max_addr < max_addr) { int min_agaw; @@ -3669,19 +3671,19 @@ static int intel_iommu_map_range(struct iommu_domain *domain, return ret; } -static void intel_iommu_unmap_range(struct iommu_domain *domain, - unsigned long iova, size_t size) +static int intel_iommu_unmap(struct iommu_domain *domain, + unsigned long iova, int gfp_order) { struct dmar_domain *dmar_domain = domain->priv; - - if (!size) - return; + size_t size = PAGE_SIZE << gfp_order; dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT, (iova + size - 1) >> VTD_PAGE_SHIFT); if (dmar_domain->max_addr == iova + size) dmar_domain->max_addr = iova; + + return gfp_order; } static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, @@ -3714,8 +3716,8 @@ static struct iommu_ops intel_iommu_ops = { .domain_destroy = intel_iommu_domain_destroy, .attach_dev = intel_iommu_attach_device, .detach_dev = intel_iommu_detach_device, - .map = intel_iommu_map_range, - .unmap = intel_iommu_unmap_range, + .map = intel_iommu_map, + .unmap = intel_iommu_unmap, .iova_to_phys = intel_iommu_iova_to_phys, .domain_has_cap = intel_iommu_domain_has_cap, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 37499127c801..1df7c508814e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1503,7 +1503,7 @@ int pci_prepare_to_sleep(struct pci_dev *dev) * pci_back_from_sleep - turn PCI device on during system-wide transition into working state * @dev: Device to handle. * - * Disable device's sytem wake-up capability and put it into D0. + * Disable device's system wake-up capability and put it into D0. */ int pci_back_from_sleep(struct pci_dev *dev) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4eb10f48d270..f8077b3c8c8c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -244,7 +244,7 @@ struct pci_ats { int stu; /* Smallest Translation Unit */ int qdep; /* Invalidate Queue Depth */ int ref_cnt; /* Physical Function reference count */ - int is_enabled:1; /* Enable bit is set */ + unsigned int is_enabled:1; /* Enable bit is set */ }; #ifdef CONFIG_PCI_IOV diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index d189e4743e69..d0f5ad306078 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -49,26 +49,6 @@ config PCMCIA_LOAD_CIS If unsure, say Y. -config PCMCIA_IOCTL - bool "PCMCIA control ioctl (obsolete)" - depends on PCMCIA && ARM && !SMP && !PREEMPT - default y - help - If you say Y here, the deprecated ioctl interface to the PCMCIA - subsystem will be built. It is needed by the deprecated pcmcia-cs - tools (cardmgr, cardctl) to function properly. - - You should use the new pcmciautils package instead (see - <file:Documentation/Changes> for location and details). - - This config option will most likely be removed from kernel 2.6.35, - the associated code from kernel 2.6.36. - - As the PCMCIA ioctl is not locking safe, it depends on !SMP and - !PREEMPT. - - If unsure, say N. - config CARDBUS bool "32-bit CardBus support" depends on PCI @@ -234,7 +214,8 @@ config PCMCIA_PXA2XX depends on ARM && ARCH_PXA && PCMCIA depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ - || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2) + || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ + || MACH_VPAC270) select PCMCIA_SOC_COMMON help Say Y here to include support for the PXA2xx PCMCIA controller @@ -317,7 +298,7 @@ config ELECTRA_CF PA Semi Electra eval board. config PCCARD_NONSTATIC - tristate + bool config PCCARD_IODYN bool diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 381b031d9d75..d006e8beab9c 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -2,15 +2,18 @@ # Makefile for the kernel pcmcia subsystem (c/o David Hinds) # -pcmcia_core-y += cs.o rsrc_mgr.o socket_sysfs.o +pcmcia_core-y += cs.o socket_sysfs.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o obj-$(CONFIG_PCCARD) += pcmcia_core.o -pcmcia-y += ds.o pcmcia_resource.o cistpl.o +pcmcia-y += ds.o pcmcia_resource.o cistpl.o pcmcia_cis.o pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o obj-$(CONFIG_PCMCIA) += pcmcia.o -obj-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o +pcmcia_rsrc-y += rsrc_mgr.o +pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o +pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o +obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o # socket drivers @@ -66,6 +69,7 @@ pxa2xx-obj-$(CONFIG_MACH_PALMTC) += pxa2xx_palmtc.o pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o +pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c index 9e84d039de41..eae9cbe37a3e 100644 --- a/drivers/pcmcia/bfin_cf_pcmcia.c +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -113,7 +113,7 @@ static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp) if (bfin_cf_present(cf->cd_pfx)) { *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; - s->irq.AssignedIRQ = 0; + s->pcmcia_irq = 0; s->pci_irq = cf->irq; } else diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index e6ab2a47d8cb..9a58862f1401 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -94,7 +94,6 @@ int __ref cb_alloc(struct pcmcia_socket *s) pci_enable_bridges(bus); pci_bus_add_devices(bus); - s->irq.AssignedIRQ = s->pci_irq; return 0; } diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 854959cada3a..60d428be0b07 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -129,6 +129,8 @@ static void __iomem *set_cis_map(struct pcmcia_socket *s, /** * pcmcia_read_cis_mem() - low-level function to read CIS memory + * + * must be called with ops_mutex held */ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) @@ -138,7 +140,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len); - mutex_lock(&s->ops_mutex); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed locations in common memory */ @@ -153,7 +154,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); memset(ptr, 0xff, len); - mutex_unlock(&s->ops_mutex); return -1; } @@ -184,7 +184,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); memset(ptr, 0xff, len); - mutex_unlock(&s->ops_mutex); return -1; } end = sys + s->map_size; @@ -198,7 +197,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, addr = 0; } } - mutex_unlock(&s->ops_mutex); dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n", *(u_char *)(ptr+0), *(u_char *)(ptr+1), *(u_char *)(ptr+2), *(u_char *)(ptr+3)); @@ -209,7 +207,8 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, /** * pcmcia_write_cis_mem() - low-level function to write CIS memory * - * Probably only useful for writing one-byte registers. + * Probably only useful for writing one-byte registers. Must be called + * with ops_mutex held. */ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) @@ -220,7 +219,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len); - mutex_lock(&s->ops_mutex); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed locations in common memory */ @@ -234,7 +232,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, ((cis_width) ? MAP_16BIT : 0)); if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); - mutex_unlock(&s->ops_mutex); return; /* FIXME: Error */ } @@ -260,7 +257,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, sys = set_cis_map(s, card_offset, flags); if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); - mutex_unlock(&s->ops_mutex); return; /* FIXME: error */ } @@ -275,7 +271,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, addr = 0; } } - mutex_unlock(&s->ops_mutex); } @@ -314,7 +309,6 @@ static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, return 0; } } - mutex_unlock(&s->ops_mutex); ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr); @@ -326,11 +320,11 @@ static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, cis->len = len; cis->attr = attr; memcpy(cis->cache, ptr, len); - mutex_lock(&s->ops_mutex); list_add(&cis->node, &s->cis_cache); - mutex_unlock(&s->ops_mutex); } } + mutex_unlock(&s->ops_mutex); + return ret; } @@ -386,6 +380,7 @@ int verify_cis_cache(struct pcmcia_socket *s) "no memory for verifying CIS\n"); return -ENOMEM; } + mutex_lock(&s->ops_mutex); list_for_each_entry(cis, &s->cis_cache, node) { int len = cis->len; @@ -395,10 +390,12 @@ int verify_cis_cache(struct pcmcia_socket *s) ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); if (ret || memcmp(buf, cis->cache, len) != 0) { kfree(buf); + mutex_unlock(&s->ops_mutex); return -1; } } kfree(buf); + mutex_unlock(&s->ops_mutex); return 0; } @@ -1362,106 +1359,6 @@ EXPORT_SYMBOL(pcmcia_parse_tuple); /** - * pccard_read_tuple() - internal CIS tuple access - * @s: the struct pcmcia_socket where the card is inserted - * @function: the device function we loop for - * @code: which CIS code shall we look for? - * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) - * - * pccard_read_tuple() reads out one tuple and attempts to parse it - */ -int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, - cisdata_t code, void *parse) -{ - tuple_t tuple; - cisdata_t *buf; - int ret; - - buf = kmalloc(256, GFP_KERNEL); - if (buf == NULL) { - dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); - return -ENOMEM; - } - tuple.DesiredTuple = code; - tuple.Attributes = 0; - if (function == BIND_FN_ALL) - tuple.Attributes = TUPLE_RETURN_COMMON; - ret = pccard_get_first_tuple(s, function, &tuple); - if (ret != 0) - goto done; - tuple.TupleData = buf; - tuple.TupleOffset = 0; - tuple.TupleDataMax = 255; - ret = pccard_get_tuple_data(s, &tuple); - if (ret != 0) - goto done; - ret = pcmcia_parse_tuple(&tuple, parse); -done: - kfree(buf); - return ret; -} - - -/** - * pccard_loop_tuple() - loop over tuples in the CIS - * @s: the struct pcmcia_socket where the card is inserted - * @function: the device function we loop for - * @code: which CIS code shall we look for? - * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) - * @priv_data: private data to be passed to the loop_tuple function. - * @loop_tuple: function to call for each CIS entry of type @function. IT - * gets passed the raw tuple, the paresed tuple (if @parse is - * set) and @priv_data. - * - * pccard_loop_tuple() loops over all CIS entries of type @function, and - * calls the @loop_tuple function for each entry. If the call to @loop_tuple - * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. - */ -int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, - cisdata_t code, cisparse_t *parse, void *priv_data, - int (*loop_tuple) (tuple_t *tuple, - cisparse_t *parse, - void *priv_data)) -{ - tuple_t tuple; - cisdata_t *buf; - int ret; - - buf = kzalloc(256, GFP_KERNEL); - if (buf == NULL) { - dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); - return -ENOMEM; - } - - tuple.TupleData = buf; - tuple.TupleDataMax = 255; - tuple.TupleOffset = 0; - tuple.DesiredTuple = code; - tuple.Attributes = 0; - - ret = pccard_get_first_tuple(s, function, &tuple); - while (!ret) { - if (pccard_get_tuple_data(s, &tuple)) - goto next_entry; - - if (parse) - if (pcmcia_parse_tuple(&tuple, parse)) - goto next_entry; - - ret = loop_tuple(&tuple, parse, priv_data); - if (!ret) - break; - -next_entry: - ret = pccard_get_next_tuple(s, function, &tuple); - } - - kfree(buf); - return ret; -} - - -/** * pccard_validate_cis() - check whether card has a sensible CIS * @s: the struct pcmcia_socket we are to check * @info: returns the number of tuples in the (valid) CIS, or 0 diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index c3383750e333..976d80706eae 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -337,7 +337,6 @@ static void socket_shutdown(struct pcmcia_socket *s) s->socket = dead_socket; s->ops->init(s); s->ops->set_socket(s, &s->socket); - s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; kfree(s->fake_cis); s->fake_cis = NULL; diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index f95864c2191e..4126a75445ea 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -52,13 +52,11 @@ struct cis_cache_entry { struct pccard_resource_ops { int (*validate_mem) (struct pcmcia_socket *s); - int (*adjust_io_region) (struct resource *res, - unsigned long r_start, - unsigned long r_end, - struct pcmcia_socket *s); - struct resource* (*find_io) (unsigned long base, int num, - unsigned long align, - struct pcmcia_socket *s); + int (*find_io) (struct pcmcia_socket *s, + unsigned int attr, + unsigned int *base, + unsigned int num, + unsigned int align); struct resource* (*find_mem) (unsigned long base, unsigned long num, unsigned long align, int low, struct pcmcia_socket *s); @@ -89,6 +87,14 @@ struct pccard_resource_ops { /* + * Stuff internal to module "pcmcia_rsrc": + */ +extern int static_init(struct pcmcia_socket *s); +extern struct resource *pcmcia_make_resource(unsigned long start, + unsigned long end, + int flags, const char *name); + +/* * Stuff internal to module "pcmcia_core": */ @@ -149,6 +155,8 @@ extern struct resource *pcmcia_find_mem_region(u_long base, int low, struct pcmcia_socket *s); +void pcmcia_cleanup_irq(struct pcmcia_socket *s); +int pcmcia_setup_irq(struct pcmcia_device *p_dev); /* cistpl.c */ extern struct bin_attribute pccard_cis_attr; diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 041eee43fd8d..7ef7adee5e4f 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -371,8 +371,6 @@ static int pcmcia_device_remove(struct device *dev) if (p_drv->remove) p_drv->remove(p_dev); - p_dev->dev_node = NULL; - /* check for proper unloading */ if (p_dev->_irq || p_dev->_io || p_dev->_locked) dev_printk(KERN_INFO, dev, @@ -479,15 +477,6 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) } -/* device_add_lock is needed to avoid double registration by cardmgr and kernel. - * Serializes pcmcia_device_add; will most likely be removed in future. - * - * While it has the caveat that adding new PCMCIA devices inside(!) device_register() - * won't work, this doesn't matter much at the moment: the driver core doesn't - * support it either. - */ -static DEFINE_MUTEX(device_add_lock); - struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev, *tmp_dev; @@ -497,8 +486,6 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu if (!s) return NULL; - mutex_lock(&device_add_lock); - pr_debug("adding device to %d, function %d\n", s->sock, function); p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL); @@ -538,8 +525,8 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu /* * p_dev->function_config must be the same for all card functions. - * Note that this is serialized by the device_add_lock, so that - * only one such struct will be created. + * Note that this is serialized by ops_mutex, so that only one + * such struct will be created. */ list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) if (p_dev->func == tmp_dev->func) { @@ -552,28 +539,31 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu /* Add to the list in pcmcia_bus_socket */ list_add(&p_dev->socket_device_list, &s->devices_list); - mutex_unlock(&s->ops_mutex); + if (pcmcia_setup_irq(p_dev)) + dev_warn(&p_dev->dev, + "IRQ setup failed -- device might not work\n"); if (!p_dev->function_config) { dev_dbg(&p_dev->dev, "creating config_t\n"); p_dev->function_config = kzalloc(sizeof(struct config_t), GFP_KERNEL); - if (!p_dev->function_config) + if (!p_dev->function_config) { + mutex_unlock(&s->ops_mutex); goto err_unreg; + } kref_init(&p_dev->function_config->ref); } + mutex_unlock(&s->ops_mutex); dev_printk(KERN_NOTICE, &p_dev->dev, - "pcmcia: registering new device %s\n", - p_dev->devname); + "pcmcia: registering new device %s (IRQ: %d)\n", + p_dev->devname, p_dev->irq); pcmcia_device_query(p_dev); if (device_register(&p_dev->dev)) goto err_unreg; - mutex_unlock(&device_add_lock); - return p_dev; err_unreg: @@ -591,7 +581,6 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu kfree(p_dev->devname); kfree(p_dev); err_put: - mutex_unlock(&device_add_lock); pcmcia_put_socket(s); return NULL; @@ -1258,6 +1247,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) handle_event(skt, event); mutex_lock(&s->ops_mutex); destroy_cis_cache(s); + pcmcia_cleanup_irq(s); mutex_unlock(&s->ops_mutex); break; diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index a7cfc7964c7c..0ad06a3bd562 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -117,7 +117,7 @@ static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp) *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; cf = container_of(s, struct omap_cf_socket, socket); - s->irq.AssignedIRQ = 0; + s->pcmcia_irq = 0; s->pci_irq = cf->irq; } else *sp = 0; diff --git a/drivers/pcmcia/pcmcia_cis.c b/drivers/pcmcia/pcmcia_cis.c new file mode 100644 index 000000000000..4a65eaf96b0a --- /dev/null +++ b/drivers/pcmcia/pcmcia_cis.c @@ -0,0 +1,356 @@ +/* + * PCMCIA high-level CIS access functions + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Copyright (C) 1999 David A. Hinds + * Copyright (C) 2004-2009 Dominik Brodowski + * + * 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 <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ss.h> +#include <pcmcia/cs.h> +#include <pcmcia/ds.h> +#include "cs_internal.h" + + +/** + * pccard_read_tuple() - internal CIS tuple access + * @s: the struct pcmcia_socket where the card is inserted + * @function: the device function we loop for + * @code: which CIS code shall we look for? + * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) + * + * pccard_read_tuple() reads out one tuple and attempts to parse it + */ +int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, + cisdata_t code, void *parse) +{ + tuple_t tuple; + cisdata_t *buf; + int ret; + + buf = kmalloc(256, GFP_KERNEL); + if (buf == NULL) { + dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); + return -ENOMEM; + } + tuple.DesiredTuple = code; + tuple.Attributes = 0; + if (function == BIND_FN_ALL) + tuple.Attributes = TUPLE_RETURN_COMMON; + ret = pccard_get_first_tuple(s, function, &tuple); + if (ret != 0) + goto done; + tuple.TupleData = buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + ret = pccard_get_tuple_data(s, &tuple); + if (ret != 0) + goto done; + ret = pcmcia_parse_tuple(&tuple, parse); +done: + kfree(buf); + return ret; +} + + +/** + * pccard_loop_tuple() - loop over tuples in the CIS + * @s: the struct pcmcia_socket where the card is inserted + * @function: the device function we loop for + * @code: which CIS code shall we look for? + * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) + * @priv_data: private data to be passed to the loop_tuple function. + * @loop_tuple: function to call for each CIS entry of type @function. IT + * gets passed the raw tuple, the paresed tuple (if @parse is + * set) and @priv_data. + * + * pccard_loop_tuple() loops over all CIS entries of type @function, and + * calls the @loop_tuple function for each entry. If the call to @loop_tuple + * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. + */ +int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, + cisdata_t code, cisparse_t *parse, void *priv_data, + int (*loop_tuple) (tuple_t *tuple, + cisparse_t *parse, + void *priv_data)) +{ + tuple_t tuple; + cisdata_t *buf; + int ret; + + buf = kzalloc(256, GFP_KERNEL); + if (buf == NULL) { + dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); + return -ENOMEM; + } + + tuple.TupleData = buf; + tuple.TupleDataMax = 255; + tuple.TupleOffset = 0; + tuple.DesiredTuple = code; + tuple.Attributes = 0; + + ret = pccard_get_first_tuple(s, function, &tuple); + while (!ret) { + if (pccard_get_tuple_data(s, &tuple)) + goto next_entry; + + if (parse) + if (pcmcia_parse_tuple(&tuple, parse)) + goto next_entry; + + ret = loop_tuple(&tuple, parse, priv_data); + if (!ret) + break; + +next_entry: + ret = pccard_get_next_tuple(s, function, &tuple); + } + + kfree(buf); + return ret; +} + +struct pcmcia_cfg_mem { + struct pcmcia_device *p_dev; + void *priv_data; + int (*conf_check) (struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data); + cisparse_t parse; + cistpl_cftable_entry_t dflt; +}; + +/** + * pcmcia_do_loop_config() - internal helper for pcmcia_loop_config() + * + * pcmcia_do_loop_config() is the internal callback for the call from + * pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred + * by a struct pcmcia_cfg_mem. + */ +static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv) +{ + cistpl_cftable_entry_t *cfg = &parse->cftable_entry; + struct pcmcia_cfg_mem *cfg_mem = priv; + + /* default values */ + cfg_mem->p_dev->conf.ConfigIndex = cfg->index; + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) + cfg_mem->dflt = *cfg; + + return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt, + cfg_mem->p_dev->socket->socket.Vcc, + cfg_mem->priv_data); +} + +/** + * pcmcia_loop_config() - loop over configuration options + * @p_dev: the struct pcmcia_device which we need to loop for. + * @conf_check: function to call for each configuration option. + * It gets passed the struct pcmcia_device, the CIS data + * describing the configuration option, and private data + * being passed to pcmcia_loop_config() + * @priv_data: private data to be passed to the conf_check function. + * + * pcmcia_loop_config() loops over all configuration options, and calls + * the driver-specific conf_check() for each one, checking whether + * it is a valid one. Returns 0 on success or errorcode otherwise. + */ +int pcmcia_loop_config(struct pcmcia_device *p_dev, + int (*conf_check) (struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data), + void *priv_data) +{ + struct pcmcia_cfg_mem *cfg_mem; + int ret; + + cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL); + if (cfg_mem == NULL) + return -ENOMEM; + + cfg_mem->p_dev = p_dev; + cfg_mem->conf_check = conf_check; + cfg_mem->priv_data = priv_data; + + ret = pccard_loop_tuple(p_dev->socket, p_dev->func, + CISTPL_CFTABLE_ENTRY, &cfg_mem->parse, + cfg_mem, pcmcia_do_loop_config); + + kfree(cfg_mem); + return ret; +} +EXPORT_SYMBOL(pcmcia_loop_config); + + +struct pcmcia_loop_mem { + struct pcmcia_device *p_dev; + void *priv_data; + int (*loop_tuple) (struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data); +}; + +/** + * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config() + * + * pcmcia_do_loop_tuple() is the internal callback for the call from + * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred + * by a struct pcmcia_cfg_mem. + */ +static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv) +{ + struct pcmcia_loop_mem *loop = priv; + + return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data); +}; + +/** + * pcmcia_loop_tuple() - loop over tuples in the CIS + * @p_dev: the struct pcmcia_device which we need to loop for. + * @code: which CIS code shall we look for? + * @priv_data: private data to be passed to the loop_tuple function. + * @loop_tuple: function to call for each CIS entry of type @function. IT + * gets passed the raw tuple and @priv_data. + * + * pcmcia_loop_tuple() loops over all CIS entries of type @function, and + * calls the @loop_tuple function for each entry. If the call to @loop_tuple + * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. + */ +int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, + int (*loop_tuple) (struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data), + void *priv_data) +{ + struct pcmcia_loop_mem loop = { + .p_dev = p_dev, + .loop_tuple = loop_tuple, + .priv_data = priv_data}; + + return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL, + &loop, pcmcia_do_loop_tuple); +} +EXPORT_SYMBOL(pcmcia_loop_tuple); + + +struct pcmcia_loop_get { + size_t len; + cisdata_t **buf; +}; + +/** + * pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple() + * + * pcmcia_do_get_tuple() is the internal callback for the call from + * pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in + * the first tuple, return 0 unconditionally. Create a memory buffer large + * enough to hold the content of the tuple, and fill it with the tuple data. + * The caller is responsible to free the buffer. + */ +static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple, + void *priv) +{ + struct pcmcia_loop_get *get = priv; + + *get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL); + if (*get->buf) { + get->len = tuple->TupleDataLen; + memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen); + } else + dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n"); + return 0; +} + +/** + * pcmcia_get_tuple() - get first tuple from CIS + * @p_dev: the struct pcmcia_device which we need to loop for. + * @code: which CIS code shall we look for? + * @buf: pointer to store the buffer to. + * + * pcmcia_get_tuple() gets the content of the first CIS entry of type @code. + * It returns the buffer length (or zero). The caller is responsible to free + * the buffer passed in @buf. + */ +size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code, + unsigned char **buf) +{ + struct pcmcia_loop_get get = { + .len = 0, + .buf = buf, + }; + + *get.buf = NULL; + pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get); + + return get.len; +} +EXPORT_SYMBOL(pcmcia_get_tuple); + + +/** + * pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis() + * + * pcmcia_do_get_mac() is the internal callback for the call from + * pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the + * tuple contains a proper LAN_NODE_ID of length 6, and copy the data + * to struct net_device->dev_addr[i]. + */ +static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple, + void *priv) +{ + struct net_device *dev = priv; + int i; + + if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) + return -EINVAL; + if (tuple->TupleDataLen < ETH_ALEN + 2) { + dev_warn(&p_dev->dev, "Invalid CIS tuple length for " + "LAN_NODE_ID\n"); + return -EINVAL; + } + + if (tuple->TupleData[1] != ETH_ALEN) { + dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n"); + return -EINVAL; + } + for (i = 0; i < 6; i++) + dev->dev_addr[i] = tuple->TupleData[i+2]; + return 0; +} + +/** + * pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE + * @p_dev: the struct pcmcia_device for which we want the address. + * @dev: a properly prepared struct net_device to store the info to. + * + * pcmcia_get_mac_from_cis() reads out the hardware MAC address from + * CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which + * must be set up properly by the driver (see examples!). + */ +int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev) +{ + return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev); +} +EXPORT_SYMBOL(pcmcia_get_mac_from_cis); + diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index 7631faa0cadd..ef0c5f133691 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -301,7 +301,9 @@ static int pccard_get_status(struct pcmcia_socket *s, (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { u_char reg; if (c->CardValues & PRESENT_PIN_REPLACE) { + mutex_lock(&s->ops_mutex); pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); + mutex_unlock(&s->ops_mutex); status->CardState |= (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; status->CardState |= @@ -315,7 +317,9 @@ static int pccard_get_status(struct pcmcia_socket *s, status->CardState |= CS_EVENT_READY_CHANGE; } if (c->CardValues & PRESENT_EXT_STATUS) { + mutex_lock(&s->ops_mutex); pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); + mutex_unlock(&s->ops_mutex); status->CardState |= (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; } @@ -351,7 +355,7 @@ static int pccard_get_configuration_info(struct pcmcia_socket *s, if (s->state & SOCKET_CARDBUS_CONFIG) { config->Attributes = CONF_VALID_CLIENT; config->IntType = INT_CARDBUS; - config->AssignedIRQ = s->irq.AssignedIRQ; + config->AssignedIRQ = s->pcmcia_irq; if (config->AssignedIRQ) config->Attributes |= CONF_ENABLE_IRQ; if (s->io[0].res) { @@ -391,7 +395,7 @@ static int pccard_get_configuration_info(struct pcmcia_socket *s, config->ExtStatus = c->ExtStatus; config->Present = config->CardValues = c->CardValues; config->IRQAttributes = c->irq.Attributes; - config->AssignedIRQ = s->irq.AssignedIRQ; + config->AssignedIRQ = s->pcmcia_irq; config->BasePort1 = c->io.BasePort1; config->NumPorts1 = c->io.NumPorts1; config->Attributes1 = c->io.Attributes1; @@ -571,7 +575,6 @@ static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s) static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first) { - dev_node_t *node; struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; int ret = 0; @@ -633,21 +636,13 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int goto err_put; } - if (first) - node = p_dev->dev_node; - else - for (node = p_dev->dev_node; node; node = node->next) - if (node == bind_info->next) - break; - if (!node) { + if (!first) { ret = -ENODEV; goto err_put; } - strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN); - bind_info->major = node->major; - bind_info->minor = node->minor; - bind_info->next = node->next; + strlcpy(bind_info->name, dev_name(&p_dev->dev), DEV_NAME_LEN); + bind_info->next = NULL; err_put: pcmcia_put_dev(p_dev); diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 7c3d03bb4f30..29f91fac1dff 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -23,6 +23,8 @@ #include <linux/netdevice.h> #include <linux/slab.h> +#include <asm/irq.h> + #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> @@ -38,29 +40,6 @@ static int io_speed; module_param(io_speed, int, 0444); -#ifdef CONFIG_PCMCIA_PROBE -#include <asm/irq.h> -/* mask of IRQs already reserved by other cards, we should avoid using them */ -static u8 pcmcia_used_irq[NR_IRQS]; -#endif - -static int pcmcia_adjust_io_region(struct resource *res, unsigned long start, - unsigned long end, struct pcmcia_socket *s) -{ - if (s->resource_ops->adjust_io_region) - return s->resource_ops->adjust_io_region(res, start, end, s); - return -ENOMEM; -} - -static struct resource *pcmcia_find_io_region(unsigned long base, int num, - unsigned long align, - struct pcmcia_socket *s) -{ - if (s->resource_ops->find_io) - return s->resource_ops->find_io(base, num, align, s); - return NULL; -} - int pcmcia_validate_mem(struct pcmcia_socket *s) { if (s->resource_ops->validate_mem) @@ -86,8 +65,7 @@ struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, static int alloc_io_space(struct pcmcia_socket *s, u_int attr, unsigned int *base, unsigned int num, u_int lines) { - int i; - unsigned int try, align; + unsigned int align; align = (*base) ? (lines ? 1<<lines : 0) : 1; if (align && (align < num)) { @@ -104,50 +82,8 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr, *base, align); align = 0; } - if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { - *base = s->io_offset | (*base & 0x0fff); - return 0; - } - /* Check for an already-allocated window that must conflict with - * what was asked for. It is a hack because it does not catch all - * potential conflicts, just the most obvious ones. - */ - for (i = 0; i < MAX_IO_WIN; i++) - if ((s->io[i].res) && *base && - ((s->io[i].res->start & (align-1)) == *base)) - return 1; - for (i = 0; i < MAX_IO_WIN; i++) { - if (!s->io[i].res) { - s->io[i].res = pcmcia_find_io_region(*base, num, align, s); - if (s->io[i].res) { - *base = s->io[i].res->start; - s->io[i].res->flags = (s->io[i].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS); - s->io[i].InUse = num; - break; - } else - return 1; - } else if ((s->io[i].res->flags & IORESOURCE_BITS) != (attr & IORESOURCE_BITS)) - continue; - /* Try to extend top of window */ - try = s->io[i].res->end + 1; - if ((*base == 0) || (*base == try)) - if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start, - s->io[i].res->end + num, s) == 0) { - *base = try; - s->io[i].InUse += num; - break; - } - /* Try to extend bottom of window */ - try = s->io[i].res->start - num; - if ((*base == 0) || (*base == try)) - if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num, - s->io[i].res->end, s) == 0) { - *base = try; - s->io[i].InUse += num; - break; - } - } - return (i == MAX_IO_WIN); + + return s->resource_ops->find_io(s, attr, base, num, align); } /* alloc_io_space */ @@ -187,6 +123,7 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, config_t *c; int addr; u_char val; + int ret = 0; if (!p_dev || !p_dev->function_config) return -EINVAL; @@ -203,11 +140,10 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, } addr = (c->ConfigBase + reg->Offset) >> 1; - mutex_unlock(&s->ops_mutex); switch (reg->Action) { case CS_READ: - pcmcia_read_cis_mem(s, 1, addr, 1, &val); + ret = pcmcia_read_cis_mem(s, 1, addr, 1, &val); reg->Value = val; break; case CS_WRITE: @@ -216,10 +152,11 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, break; default: dev_dbg(&s->dev, "Invalid conf register request\n"); - return -EINVAL; + ret = -EINVAL; break; } - return 0; + mutex_unlock(&s->ops_mutex); + return ret; } /* pcmcia_access_configuration_register */ EXPORT_SYMBOL(pcmcia_access_configuration_register); @@ -275,19 +212,9 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, goto unlock; } - if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { - if (mod->Attributes & CONF_ENABLE_IRQ) { - c->Attributes |= CONF_ENABLE_IRQ; - s->socket.io_irq = s->irq.AssignedIRQ; - } else { - c->Attributes &= ~CONF_ENABLE_IRQ; - s->socket.io_irq = 0; - } - s->ops->set_socket(s, &s->socket); - } - - if (mod->Attributes & CONF_VCC_CHANGE_VALID) { - dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n"); + if (mod->Attributes & (CONF_IRQ_CHANGE_VALID | CONF_VCC_CHANGE_VALID)) { + dev_dbg(&s->dev, + "changing Vcc or IRQ is not allowed at this time\n"); ret = -EINVAL; goto unlock; } @@ -422,52 +349,6 @@ out: } /* pcmcia_release_io */ -static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) -{ - struct pcmcia_socket *s = p_dev->socket; - config_t *c; - int ret = -EINVAL; - - mutex_lock(&s->ops_mutex); - - c = p_dev->function_config; - - if (!p_dev->_irq) - goto out; - - p_dev->_irq = 0; - - if (c->state & CONFIG_LOCKED) - goto out; - - if (c->irq.Attributes != req->Attributes) { - dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n"); - goto out; - } - if (s->irq.AssignedIRQ != req->AssignedIRQ) { - dev_dbg(&s->dev, "IRQ must match assigned one\n"); - goto out; - } - if (--s->irq.Config == 0) { - c->state &= ~CONFIG_IRQ_REQ; - s->irq.AssignedIRQ = 0; - } - - if (req->Handler) - free_irq(req->AssignedIRQ, p_dev->priv); - -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[req->AssignedIRQ]--; -#endif - ret = 0; - -out: - mutex_unlock(&s->ops_mutex); - - return ret; -} /* pcmcia_release_irq */ - - int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh) { struct pcmcia_socket *s = p_dev->socket; @@ -551,12 +432,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (req->Attributes & CONF_ENABLE_SPKR) s->socket.flags |= SS_SPKR_ENA; if (req->Attributes & CONF_ENABLE_IRQ) - s->socket.io_irq = s->irq.AssignedIRQ; + s->socket.io_irq = s->pcmcia_irq; else s->socket.io_irq = 0; s->ops->set_socket(s, &s->socket); s->lock_count++; - mutex_unlock(&s->ops_mutex); /* Set up CIS configuration registers */ base = c->ConfigBase = req->ConfigBase; @@ -574,9 +454,9 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (req->Present & PRESENT_IOBASE_0) c->Option |= COR_ADDR_DECODE; } - if (c->state & CONFIG_IRQ_REQ) - if (!(c->irq.Attributes & IRQ_FORCED_PULSE)) - c->Option |= COR_LEVEL_REQ; + if ((req->Attributes & CONF_ENABLE_IRQ) && + !(req->Attributes & CONF_ENABLE_PULSE_IRQ)) + c->Option |= COR_LEVEL_REQ; pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option); mdelay(40); } @@ -605,7 +485,6 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, /* Configure I/O windows */ if (c->state & CONFIG_IO_REQ) { - mutex_lock(&s->ops_mutex); iomap.speed = io_speed; for (i = 0; i < MAX_IO_WIN; i++) if (s->io[i].res) { @@ -624,11 +503,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, s->ops->set_io_map(s, &iomap); s->io[i].Config++; } - mutex_unlock(&s->ops_mutex); } c->state |= CONFIG_LOCKED; p_dev->_locked = 1; + mutex_unlock(&s->ops_mutex); return 0; } /* pcmcia_request_configuration */ EXPORT_SYMBOL(pcmcia_request_configuration); @@ -706,137 +585,176 @@ out: EXPORT_SYMBOL(pcmcia_request_io); -/** pcmcia_request_irq +/** + * pcmcia_request_irq() - attempt to request a IRQ for a PCMCIA device * - * Request_irq() reserves an irq for this client. + * pcmcia_request_irq() is a wrapper around request_irq which will allow + * the PCMCIA core to clean up the registration in pcmcia_disable_device(). + * Drivers are free to use request_irq() directly, but then they need to + * call free_irq themselfves, too. Also, only IRQF_SHARED capable IRQ + * handlers are allowed. + */ +int __must_check pcmcia_request_irq(struct pcmcia_device *p_dev, + irq_handler_t handler) +{ + int ret; + + if (!p_dev->irq) + return -EINVAL; + + ret = request_irq(p_dev->irq, handler, IRQF_SHARED, + p_dev->devname, p_dev->priv); + if (!ret) + p_dev->_irq = 1; + + return ret; +} +EXPORT_SYMBOL(pcmcia_request_irq); + + +/** + * pcmcia_request_exclusive_irq() - attempt to request an exclusive IRQ first * - * Also, since Linux only reserves irq's when they are actually - * hooked, we don't guarantee that an irq will still be available - * when the configuration is locked. Now that I think about it, - * there might be a way to fix this using a dummy handler. + * pcmcia_request_exclusive_irq() is a wrapper around request_irq which + * attempts first to request an exclusive IRQ. If it fails, it also accepts + * a shared IRQ, but prints out a warning. PCMCIA drivers should allow for + * IRQ sharing and either use request_irq directly (then they need to call + * free_irq themselves, too), or the pcmcia_request_irq() function. */ +int __must_check +__pcmcia_request_exclusive_irq(struct pcmcia_device *p_dev, + irq_handler_t handler) +{ + int ret; + + if (!p_dev->irq) + return -EINVAL; + + ret = request_irq(p_dev->irq, handler, 0, p_dev->devname, p_dev->priv); + if (ret) { + ret = pcmcia_request_irq(p_dev, handler); + dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: " + "request for exclusive IRQ could not be fulfilled.\n"); + dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver " + "needs updating to supported shared IRQ lines.\n"); + } + if (ret) + dev_printk(KERN_INFO, &p_dev->dev, "request_irq() failed\n"); + else + p_dev->_irq = 1; + + return ret; +} /* pcmcia_request_exclusive_irq */ +EXPORT_SYMBOL(__pcmcia_request_exclusive_irq); + #ifdef CONFIG_PCMCIA_PROBE + +/* mask of IRQs already reserved by other cards, we should avoid using them */ +static u8 pcmcia_used_irq[NR_IRQS]; + static irqreturn_t test_action(int cpl, void *dev_id) { return IRQ_NONE; } -#endif -int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) +/** + * pcmcia_setup_isa_irq() - determine whether an ISA IRQ can be used + * @p_dev - the associated PCMCIA device + * + * locking note: must be called with ops_mutex locked. + */ +static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type) { struct pcmcia_socket *s = p_dev->socket; - config_t *c; - int ret = -EINVAL, irq = 0; - int type; + unsigned int try, irq; + u32 mask = s->irq_mask; + int ret = -ENODEV; - mutex_lock(&s->ops_mutex); + for (try = 0; try < 64; try++) { + irq = try % 32; - if (!(s->state & SOCKET_PRESENT)) { - dev_dbg(&s->dev, "No card present\n"); - goto out; - } - c = p_dev->function_config; - if (c->state & CONFIG_LOCKED) { - dev_dbg(&s->dev, "Configuration is locked\n"); - goto out; - } - if (c->state & CONFIG_IRQ_REQ) { - dev_dbg(&s->dev, "IRQ already configured\n"); - goto out; + /* marked as available by driver, not blocked by userspace? */ + if (!((mask >> irq) & 1)) + continue; + + /* avoid an IRQ which is already used by another PCMCIA card */ + if ((try < 32) && pcmcia_used_irq[irq]) + continue; + + /* register the correct driver, if possible, to check whether + * registering a dummy handle works, i.e. if the IRQ isn't + * marked as used by the kernel resource management core */ + ret = request_irq(irq, test_action, type, p_dev->devname, + p_dev); + if (!ret) { + free_irq(irq, p_dev); + p_dev->irq = s->pcmcia_irq = irq; + pcmcia_used_irq[irq]++; + break; + } } - /* Decide what type of interrupt we are registering */ - type = 0; - if (s->functions > 1) /* All of this ought to be handled higher up */ - type = IRQF_SHARED; - else if (req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) - type = IRQF_SHARED; - else - printk(KERN_WARNING "pcmcia: Driver needs updating to support IRQ sharing.\n"); + return ret; +} - /* If the interrupt is already assigned, it must be the same */ - if (s->irq.AssignedIRQ != 0) - irq = s->irq.AssignedIRQ; +void pcmcia_cleanup_irq(struct pcmcia_socket *s) +{ + pcmcia_used_irq[s->pcmcia_irq]--; + s->pcmcia_irq = 0; +} -#ifdef CONFIG_PCMCIA_PROBE - if (!irq) { - int try; - u32 mask = s->irq_mask; - void *data = p_dev; /* something unique to this device */ +#else /* CONFIG_PCMCIA_PROBE */ - for (try = 0; try < 64; try++) { - irq = try % 32; +static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type) +{ + return -EINVAL; +} - /* marked as available by driver, and not blocked by userspace? */ - if (!((mask >> irq) & 1)) - continue; +void pcmcia_cleanup_irq(struct pcmcia_socket *s) +{ + s->pcmcia_irq = 0; + return; +} - /* avoid an IRQ which is already used by a PCMCIA card */ - if ((try < 32) && pcmcia_used_irq[irq]) - continue; +#endif /* CONFIG_PCMCIA_PROBE */ - /* register the correct driver, if possible, of check whether - * registering a dummy handle works, i.e. if the IRQ isn't - * marked as used by the kernel resource management core */ - ret = request_irq(irq, - (req->Handler) ? req->Handler : test_action, - type, - p_dev->devname, - (req->Handler) ? p_dev->priv : data); - if (!ret) { - if (!req->Handler) - free_irq(irq, data); - break; - } - } - } -#endif - /* only assign PCI irq if no IRQ already assigned */ - if (ret && !s->irq.AssignedIRQ) { - if (!s->pci_irq) { - dev_printk(KERN_INFO, &s->dev, "no IRQ found\n"); - goto out; - } - type = IRQF_SHARED; - irq = s->pci_irq; - } - if (ret && req->Handler) { - ret = request_irq(irq, req->Handler, type, - p_dev->devname, p_dev->priv); - if (ret) { - dev_printk(KERN_INFO, &s->dev, - "request_irq() failed\n"); - goto out; - } - } +/** + * pcmcia_setup_irq() - determine IRQ to be used for device + * @p_dev - the associated PCMCIA device + * + * locking note: must be called with ops_mutex locked. + */ +int pcmcia_setup_irq(struct pcmcia_device *p_dev) +{ + struct pcmcia_socket *s = p_dev->socket; - /* Make sure the fact the request type was overridden is passed back */ - if (type == IRQF_SHARED && !(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) { - req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING; - dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: " - "request for exclusive IRQ could not be fulfilled.\n"); - dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver " - "needs updating to supported shared IRQ lines.\n"); + if (p_dev->irq) + return 0; + + /* already assigned? */ + if (s->pcmcia_irq) { + p_dev->irq = s->pcmcia_irq; + return 0; } - c->irq.Attributes = req->Attributes; - s->irq.AssignedIRQ = req->AssignedIRQ = irq; - s->irq.Config++; - c->state |= CONFIG_IRQ_REQ; - p_dev->_irq = 1; + /* prefer an exclusive ISA irq */ + if (!pcmcia_setup_isa_irq(p_dev, 0)) + return 0; -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[irq]++; -#endif + /* but accept a shared ISA irq */ + if (!pcmcia_setup_isa_irq(p_dev, IRQF_SHARED)) + return 0; - ret = 0; -out: - mutex_unlock(&s->ops_mutex); - return ret; -} /* pcmcia_request_irq */ -EXPORT_SYMBOL(pcmcia_request_irq); + /* but use the PCI irq otherwise */ + if (s->pci_irq) { + p_dev->irq = s->pcmcia_irq = s->pci_irq; + return 0; + } + + return -EINVAL; +} /** pcmcia_request_window @@ -939,237 +857,9 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev) { pcmcia_release_configuration(p_dev); pcmcia_release_io(p_dev, &p_dev->io); - pcmcia_release_irq(p_dev, &p_dev->irq); + if (p_dev->_irq) + free_irq(p_dev->irq, p_dev->priv); if (p_dev->win) pcmcia_release_window(p_dev, p_dev->win); } EXPORT_SYMBOL(pcmcia_disable_device); - - -struct pcmcia_cfg_mem { - struct pcmcia_device *p_dev; - void *priv_data; - int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, - void *priv_data); - cisparse_t parse; - cistpl_cftable_entry_t dflt; -}; - -/** - * pcmcia_do_loop_config() - internal helper for pcmcia_loop_config() - * - * pcmcia_do_loop_config() is the internal callback for the call from - * pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred - * by a struct pcmcia_cfg_mem. - */ -static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv) -{ - cistpl_cftable_entry_t *cfg = &parse->cftable_entry; - struct pcmcia_cfg_mem *cfg_mem = priv; - - /* default values */ - cfg_mem->p_dev->conf.ConfigIndex = cfg->index; - if (cfg->flags & CISTPL_CFTABLE_DEFAULT) - cfg_mem->dflt = *cfg; - - return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt, - cfg_mem->p_dev->socket->socket.Vcc, - cfg_mem->priv_data); -} - -/** - * pcmcia_loop_config() - loop over configuration options - * @p_dev: the struct pcmcia_device which we need to loop for. - * @conf_check: function to call for each configuration option. - * It gets passed the struct pcmcia_device, the CIS data - * describing the configuration option, and private data - * being passed to pcmcia_loop_config() - * @priv_data: private data to be passed to the conf_check function. - * - * pcmcia_loop_config() loops over all configuration options, and calls - * the driver-specific conf_check() for each one, checking whether - * it is a valid one. Returns 0 on success or errorcode otherwise. - */ -int pcmcia_loop_config(struct pcmcia_device *p_dev, - int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, - void *priv_data), - void *priv_data) -{ - struct pcmcia_cfg_mem *cfg_mem; - int ret; - - cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL); - if (cfg_mem == NULL) - return -ENOMEM; - - cfg_mem->p_dev = p_dev; - cfg_mem->conf_check = conf_check; - cfg_mem->priv_data = priv_data; - - ret = pccard_loop_tuple(p_dev->socket, p_dev->func, - CISTPL_CFTABLE_ENTRY, &cfg_mem->parse, - cfg_mem, pcmcia_do_loop_config); - - kfree(cfg_mem); - return ret; -} -EXPORT_SYMBOL(pcmcia_loop_config); - - -struct pcmcia_loop_mem { - struct pcmcia_device *p_dev; - void *priv_data; - int (*loop_tuple) (struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv_data); -}; - -/** - * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config() - * - * pcmcia_do_loop_tuple() is the internal callback for the call from - * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred - * by a struct pcmcia_cfg_mem. - */ -static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv) -{ - struct pcmcia_loop_mem *loop = priv; - - return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data); -}; - -/** - * pcmcia_loop_tuple() - loop over tuples in the CIS - * @p_dev: the struct pcmcia_device which we need to loop for. - * @code: which CIS code shall we look for? - * @priv_data: private data to be passed to the loop_tuple function. - * @loop_tuple: function to call for each CIS entry of type @function. IT - * gets passed the raw tuple and @priv_data. - * - * pcmcia_loop_tuple() loops over all CIS entries of type @function, and - * calls the @loop_tuple function for each entry. If the call to @loop_tuple - * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. - */ -int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, - int (*loop_tuple) (struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv_data), - void *priv_data) -{ - struct pcmcia_loop_mem loop = { - .p_dev = p_dev, - .loop_tuple = loop_tuple, - .priv_data = priv_data}; - - return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL, - &loop, pcmcia_do_loop_tuple); -} -EXPORT_SYMBOL(pcmcia_loop_tuple); - - -struct pcmcia_loop_get { - size_t len; - cisdata_t **buf; -}; - -/** - * pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple() - * - * pcmcia_do_get_tuple() is the internal callback for the call from - * pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in - * the first tuple, return 0 unconditionally. Create a memory buffer large - * enough to hold the content of the tuple, and fill it with the tuple data. - * The caller is responsible to free the buffer. - */ -static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple, - void *priv) -{ - struct pcmcia_loop_get *get = priv; - - *get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL); - if (*get->buf) { - get->len = tuple->TupleDataLen; - memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen); - } else - dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n"); - return 0; -} - -/** - * pcmcia_get_tuple() - get first tuple from CIS - * @p_dev: the struct pcmcia_device which we need to loop for. - * @code: which CIS code shall we look for? - * @buf: pointer to store the buffer to. - * - * pcmcia_get_tuple() gets the content of the first CIS entry of type @code. - * It returns the buffer length (or zero). The caller is responsible to free - * the buffer passed in @buf. - */ -size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code, - unsigned char **buf) -{ - struct pcmcia_loop_get get = { - .len = 0, - .buf = buf, - }; - - *get.buf = NULL; - pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get); - - return get.len; -} -EXPORT_SYMBOL(pcmcia_get_tuple); - - -/** - * pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis() - * - * pcmcia_do_get_mac() is the internal callback for the call from - * pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the - * tuple contains a proper LAN_NODE_ID of length 6, and copy the data - * to struct net_device->dev_addr[i]. - */ -static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple, - void *priv) -{ - struct net_device *dev = priv; - int i; - - if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) - return -EINVAL; - if (tuple->TupleDataLen < ETH_ALEN + 2) { - dev_warn(&p_dev->dev, "Invalid CIS tuple length for " - "LAN_NODE_ID\n"); - return -EINVAL; - } - - if (tuple->TupleData[1] != ETH_ALEN) { - dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n"); - return -EINVAL; - } - for (i = 0; i < 6; i++) - dev->dev_addr[i] = tuple->TupleData[i+2]; - return 0; -} - -/** - * pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE - * @p_dev: the struct pcmcia_device for which we want the address. - * @dev: a properly prepared struct net_device to store the info to. - * - * pcmcia_get_mac_from_cis() reads out the hardware MAC address from - * CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which - * must be set up properly by the driver (see examples!). - */ -int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev) -{ - return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev); -} -EXPORT_SYMBOL(pcmcia_get_mac_from_cis); - diff --git a/drivers/pcmcia/pxa2xx_vpac270.c b/drivers/pcmcia/pxa2xx_vpac270.c new file mode 100644 index 000000000000..55627eccee8e --- /dev/null +++ b/drivers/pcmcia/pxa2xx_vpac270.c @@ -0,0 +1,229 @@ +/* + * linux/drivers/pcmcia/pxa2xx_vpac270.c + * + * Driver for Voipac PXA270 PCMCIA and CF sockets + * + * Copyright (C) 2010 + * Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/mach-types.h> + +#include <mach/gpio.h> +#include <mach/vpac270.h> + +#include "soc_common.h" + +static struct pcmcia_irqs cd_irqs[] = { + { + .sock = 0, + .irq = IRQ_GPIO(GPIO84_VPAC270_PCMCIA_CD), + .str = "PCMCIA CD" + }, + { + .sock = 1, + .irq = IRQ_GPIO(GPIO17_VPAC270_CF_CD), + .str = "CF CD" + }, +}; + +static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret; + + if (skt->nr == 0) { + ret = gpio_request(GPIO84_VPAC270_PCMCIA_CD, "PCMCIA CD"); + if (ret) + goto err1; + ret = gpio_direction_input(GPIO84_VPAC270_PCMCIA_CD); + if (ret) + goto err2; + + ret = gpio_request(GPIO35_VPAC270_PCMCIA_RDY, "PCMCIA RDY"); + if (ret) + goto err2; + ret = gpio_direction_input(GPIO35_VPAC270_PCMCIA_RDY); + if (ret) + goto err3; + + ret = gpio_request(GPIO107_VPAC270_PCMCIA_PPEN, "PCMCIA PPEN"); + if (ret) + goto err3; + ret = gpio_direction_output(GPIO107_VPAC270_PCMCIA_PPEN, 0); + if (ret) + goto err4; + + ret = gpio_request(GPIO11_VPAC270_PCMCIA_RESET, "PCMCIA RESET"); + if (ret) + goto err4; + ret = gpio_direction_output(GPIO11_VPAC270_PCMCIA_RESET, 0); + if (ret) + goto err5; + + skt->socket.pci_irq = gpio_to_irq(GPIO35_VPAC270_PCMCIA_RDY); + + return soc_pcmcia_request_irqs(skt, &cd_irqs[0], 1); + +err5: + gpio_free(GPIO11_VPAC270_PCMCIA_RESET); +err4: + gpio_free(GPIO107_VPAC270_PCMCIA_PPEN); +err3: + gpio_free(GPIO35_VPAC270_PCMCIA_RDY); +err2: + gpio_free(GPIO84_VPAC270_PCMCIA_CD); +err1: + return ret; + + } else { + ret = gpio_request(GPIO17_VPAC270_CF_CD, "CF CD"); + if (ret) + goto err6; + ret = gpio_direction_input(GPIO17_VPAC270_CF_CD); + if (ret) + goto err7; + + ret = gpio_request(GPIO12_VPAC270_CF_RDY, "CF RDY"); + if (ret) + goto err7; + ret = gpio_direction_input(GPIO12_VPAC270_CF_RDY); + if (ret) + goto err8; + + ret = gpio_request(GPIO16_VPAC270_CF_RESET, "CF RESET"); + if (ret) + goto err8; + ret = gpio_direction_output(GPIO16_VPAC270_CF_RESET, 0); + if (ret) + goto err9; + + skt->socket.pci_irq = gpio_to_irq(GPIO12_VPAC270_CF_RDY); + + return soc_pcmcia_request_irqs(skt, &cd_irqs[1], 1); + +err9: + gpio_free(GPIO16_VPAC270_CF_RESET); +err8: + gpio_free(GPIO12_VPAC270_CF_RDY); +err7: + gpio_free(GPIO17_VPAC270_CF_CD); +err6: + return ret; + + } +} + +static void vpac270_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + gpio_free(GPIO11_VPAC270_PCMCIA_RESET); + gpio_free(GPIO107_VPAC270_PCMCIA_PPEN); + gpio_free(GPIO35_VPAC270_PCMCIA_RDY); + gpio_free(GPIO84_VPAC270_PCMCIA_CD); + gpio_free(GPIO16_VPAC270_CF_RESET); + gpio_free(GPIO12_VPAC270_CF_RDY); + gpio_free(GPIO17_VPAC270_CF_CD); +} + +static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + if (skt->nr == 0) { + state->detect = !gpio_get_value(GPIO84_VPAC270_PCMCIA_CD); + state->ready = !!gpio_get_value(GPIO35_VPAC270_PCMCIA_RDY); + } else { + state->detect = !gpio_get_value(GPIO17_VPAC270_CF_CD); + state->ready = !!gpio_get_value(GPIO12_VPAC270_CF_RDY); + } + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int +vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + if (skt->nr == 0) { + gpio_set_value(GPIO11_VPAC270_PCMCIA_RESET, + (state->flags & SS_RESET)); + gpio_set_value(GPIO107_VPAC270_PCMCIA_PPEN, + !(state->Vcc == 33 || state->Vcc == 50)); + } else { + gpio_set_value(GPIO16_VPAC270_CF_RESET, + (state->flags & SS_RESET)); + } + + return 0; +} + +static void vpac270_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void vpac270_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level vpac270_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 2, + + .hw_init = vpac270_pcmcia_hw_init, + .hw_shutdown = vpac270_pcmcia_hw_shutdown, + + .socket_state = vpac270_pcmcia_socket_state, + .configure_socket = vpac270_pcmcia_configure_socket, + + .socket_init = vpac270_pcmcia_socket_init, + .socket_suspend = vpac270_pcmcia_socket_suspend, +}; + +static struct platform_device *vpac270_pcmcia_device; + +static int __init vpac270_pcmcia_init(void) +{ + int ret; + + if (!machine_is_vpac270()) + return -ENODEV; + + vpac270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!vpac270_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(vpac270_pcmcia_device, + &vpac270_pcmcia_ops, sizeof(vpac270_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(vpac270_pcmcia_device); + + if (ret) + platform_device_put(vpac270_pcmcia_device); + + return ret; +} + +static void __exit vpac270_pcmcia_exit(void) +{ + platform_device_unregister(vpac270_pcmcia_device); +} + +module_init(vpac270_pcmcia_init); +module_exit(vpac270_pcmcia_exit); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("PCMCIA support for Voipac PXA270"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/rsrc_iodyn.c b/drivers/pcmcia/rsrc_iodyn.c new file mode 100644 index 000000000000..d0bf35021065 --- /dev/null +++ b/drivers/pcmcia/rsrc_iodyn.c @@ -0,0 +1,172 @@ +/* + * rsrc_iodyn.c -- Resource management routines for MEM-static sockets. + * + * 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. + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/ss.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include "cs_internal.h" + + +struct pcmcia_align_data { + unsigned long mask; + unsigned long offset; +}; + +static resource_size_t pcmcia_align(void *align_data, + const struct resource *res, + resource_size_t size, resource_size_t align) +{ + struct pcmcia_align_data *data = align_data; + resource_size_t start; + + start = (res->start & ~data->mask) + data->offset; + if (start < res->start) + start += data->mask + 1; + +#ifdef CONFIG_X86 + if (res->flags & IORESOURCE_IO) { + if (start & 0x300) + start = (start + 0x3ff) & ~0x3ff; + } +#endif + +#ifdef CONFIG_M68K + if (res->flags & IORESOURCE_IO) { + if ((res->start + size - 1) >= 1024) + start = res->end; + } +#endif + + return start; +} + + +static struct resource *__iodyn_find_io_region(struct pcmcia_socket *s, + unsigned long base, int num, + unsigned long align) +{ + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, + dev_name(&s->dev)); + struct pcmcia_align_data data; + unsigned long min = base; + int ret; + + data.mask = align - 1; + data.offset = base & data.mask; + +#ifdef CONFIG_PCI + if (s->cb_dev) { + ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, + min, 0, pcmcia_align, &data); + } else +#endif + ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, + 1, pcmcia_align, &data); + + if (ret != 0) { + kfree(res); + res = NULL; + } + return res; +} + +static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) +{ + int i, ret = 0; + + /* Check for an already-allocated window that must conflict with + * what was asked for. It is a hack because it does not catch all + * potential conflicts, just the most obvious ones. + */ + for (i = 0; i < MAX_IO_WIN; i++) { + if (!s->io[i].res) + continue; + + if (!*base) + continue; + + if ((s->io[i].res->start & (align-1)) == *base) + return -EBUSY; + } + + for (i = 0; i < MAX_IO_WIN; i++) { + struct resource *res = s->io[i].res; + unsigned int try; + + if (res && (res->flags & IORESOURCE_BITS) != + (attr & IORESOURCE_BITS)) + continue; + + if (!res) { + if (align == 0) + align = 0x10000; + + res = s->io[i].res = __iodyn_find_io_region(s, *base, + num, align); + if (!res) + return -EINVAL; + + *base = res->start; + s->io[i].res->flags = + ((res->flags & ~IORESOURCE_BITS) | + (attr & IORESOURCE_BITS)); + s->io[i].InUse = num; + return 0; + } + + /* Try to extend top of window */ + try = res->end + 1; + if ((*base == 0) || (*base == try)) { + if (adjust_resource(s->io[i].res, res->start, + res->end - res->start + num + 1)) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + + /* Try to extend bottom of window */ + try = res->start - num; + if ((*base == 0) || (*base == try)) { + if (adjust_resource(s->io[i].res, + res->start - num, + res->end - res->start + num + 1)) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + + return -EINVAL; +} + + +struct pccard_resource_ops pccard_iodyn_ops = { + .validate_mem = NULL, + .find_io = iodyn_find_io, + .find_mem = NULL, + .add_io = NULL, + .add_mem = NULL, + .init = static_init, + .exit = NULL, +}; +EXPORT_SYMBOL(pccard_iodyn_ops); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index ffa5f3cae57b..142efac3c387 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -22,7 +22,7 @@ #include <pcmcia/cistpl.h> #include "cs_internal.h" -static int static_init(struct pcmcia_socket *s) +int static_init(struct pcmcia_socket *s) { /* the good thing about SS_CAP_STATIC_MAP sockets is * that they don't need a resource database */ @@ -32,118 +32,44 @@ static int static_init(struct pcmcia_socket *s) return 0; } - -struct pccard_resource_ops pccard_static_ops = { - .validate_mem = NULL, - .adjust_io_region = NULL, - .find_io = NULL, - .find_mem = NULL, - .add_io = NULL, - .add_mem = NULL, - .init = static_init, - .exit = NULL, -}; -EXPORT_SYMBOL(pccard_static_ops); - - -#ifdef CONFIG_PCCARD_IODYN - -static struct resource * -make_resource(unsigned long b, unsigned long n, int flags, char *name) +struct resource *pcmcia_make_resource(unsigned long start, unsigned long end, + int flags, const char *name) { struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); if (res) { res->name = name; - res->start = b; - res->end = b + n - 1; + res->start = start; + res->end = start + end - 1; res->flags = flags; } return res; } -struct pcmcia_align_data { - unsigned long mask; - unsigned long offset; -}; - -static resource_size_t pcmcia_align(void *align_data, - const struct resource *res, - resource_size_t size, resource_size_t align) +static int static_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) { - struct pcmcia_align_data *data = align_data; - resource_size_t start; + if (!s->io_offset) + return -EINVAL; + *base = s->io_offset | (*base & 0x0fff); - start = (res->start & ~data->mask) + data->offset; - if (start < res->start) - start += data->mask + 1; - -#ifdef CONFIG_X86 - if (res->flags & IORESOURCE_IO) { - if (start & 0x300) - start = (start + 0x3ff) & ~0x3ff; - } -#endif - -#ifdef CONFIG_M68K - if (res->flags & IORESOURCE_IO) { - if ((res->start + size - 1) >= 1024) - start = res->end; - } -#endif - - return start; -} - - -static int iodyn_adjust_io_region(struct resource *res, unsigned long r_start, - unsigned long r_end, struct pcmcia_socket *s) -{ - return adjust_resource(res, r_start, r_end - r_start + 1); + return 0; } -static struct resource *iodyn_find_io_region(unsigned long base, int num, - unsigned long align, struct pcmcia_socket *s) -{ - struct resource *res = make_resource(0, num, IORESOURCE_IO, - dev_name(&s->dev)); - struct pcmcia_align_data data; - unsigned long min = base; - int ret; - - if (align == 0) - align = 0x10000; - - data.mask = align - 1; - data.offset = base & data.mask; - -#ifdef CONFIG_PCI - if (s->cb_dev) { - ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, - min, 0, pcmcia_align, &data); - } else -#endif - ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, - 1, pcmcia_align, &data); - - if (ret != 0) { - kfree(res); - res = NULL; - } - return res; -} - -struct pccard_resource_ops pccard_iodyn_ops = { +struct pccard_resource_ops pccard_static_ops = { .validate_mem = NULL, - .adjust_io_region = iodyn_adjust_io_region, - .find_io = iodyn_find_io_region, + .find_io = static_find_io, .find_mem = NULL, .add_io = NULL, .add_mem = NULL, .init = static_init, .exit = NULL, }; -EXPORT_SYMBOL(pccard_iodyn_ops); +EXPORT_SYMBOL(pccard_static_ops); + -#endif /* CONFIG_PCCARD_IODYN */ +MODULE_AUTHOR("David A. Hinds, Dominik Brodowski"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("rsrc_nonstatic"); diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index a6eb7b59ba9f..dcd1a4ad3d63 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -34,8 +34,10 @@ #include <pcmcia/cistpl.h> #include "cs_internal.h" +/* moved to rsrc_mgr.c MODULE_AUTHOR("David A. Hinds, Dominik Brodowski"); MODULE_LICENSE("GPL"); +*/ /* Parameters that can be set with 'insmod' */ @@ -70,27 +72,13 @@ struct socket_data { ======================================================================*/ static struct resource * -make_resource(resource_size_t b, resource_size_t n, int flags, const char *name) -{ - struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); - - if (res) { - res->name = name; - res->start = b; - res->end = b + n - 1; - res->flags = flags; - } - return res; -} - -static struct resource * claim_region(struct pcmcia_socket *s, resource_size_t base, resource_size_t size, int type, char *name) { struct resource *res, *parent; parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource; - res = make_resource(base, size, type | IORESOURCE_BUSY, name); + res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name); if (res) { #ifdef CONFIG_PCI @@ -661,8 +649,9 @@ pcmcia_align(void *align_data, const struct resource *res, * Adjust an existing IO region allocation, but making sure that we don't * encroach outside the resources which the user supplied. */ -static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start, - unsigned long r_end, struct pcmcia_socket *s) +static int __nonstatic_adjust_io_region(struct pcmcia_socket *s, + unsigned long r_start, + unsigned long r_end) { struct resource_map *m; struct socket_data *s_data = s->resource_data; @@ -675,8 +664,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star if (start > r_start || r_end > end) continue; - ret = adjust_resource(res, r_start, r_end - r_start + 1); - break; + ret = 0; } return ret; @@ -695,18 +683,17 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ======================================================================*/ -static struct resource *nonstatic_find_io_region(unsigned long base, int num, - unsigned long align, struct pcmcia_socket *s) +static struct resource *__nonstatic_find_io_region(struct pcmcia_socket *s, + unsigned long base, int num, + unsigned long align) { - struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev)); + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, + dev_name(&s->dev)); struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min = base; int ret; - if (align == 0) - align = 0x10000; - data.mask = align - 1; data.offset = base & data.mask; data.map = &s_data->io_db; @@ -727,10 +714,97 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, return res; } +static int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) +{ + int i, ret = 0; + + /* Check for an already-allocated window that must conflict with + * what was asked for. It is a hack because it does not catch all + * potential conflicts, just the most obvious ones. + */ + for (i = 0; i < MAX_IO_WIN; i++) { + if (!s->io[i].res) + continue; + + if (!*base) + continue; + + if ((s->io[i].res->start & (align-1)) == *base) + return -EBUSY; + } + + for (i = 0; i < MAX_IO_WIN; i++) { + struct resource *res = s->io[i].res; + unsigned int try; + + if (res && (res->flags & IORESOURCE_BITS) != + (attr & IORESOURCE_BITS)) + continue; + + if (!res) { + if (align == 0) + align = 0x10000; + + res = s->io[i].res = __nonstatic_find_io_region(s, + *base, num, + align); + if (!res) + return -EINVAL; + + *base = res->start; + s->io[i].res->flags = + ((res->flags & ~IORESOURCE_BITS) | + (attr & IORESOURCE_BITS)); + s->io[i].InUse = num; + return 0; + } + + /* Try to extend top of window */ + try = res->end + 1; + if ((*base == 0) || (*base == try)) { + ret = __nonstatic_adjust_io_region(s, res->start, + res->end + num); + if (!ret) { + ret = adjust_resource(s->io[i].res, res->start, + res->end - res->start + num + 1); + if (ret) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + + /* Try to extend bottom of window */ + try = res->start - num; + if ((*base == 0) || (*base == try)) { + ret = __nonstatic_adjust_io_region(s, + res->start - num, + res->end); + if (!ret) { + ret = adjust_resource(s->io[i].res, + res->start - num, + res->end - res->start + num + 1); + if (ret) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + } + + return -EINVAL; +} + + static struct resource *nonstatic_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s) { - struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev)); + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM, + dev_name(&s->dev)); struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min, max; @@ -861,23 +935,42 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s) return -ENODEV; #if defined(CONFIG_X86) - /* If this is the root bus, the risk of hitting - * some strange system devices which aren't protected - * by either ACPI resource tables or properly requested - * resources is too big. Therefore, don't do auto-adding - * of resources at the moment. + /* If this is the root bus, the risk of hitting some strange + * system devices is too high: If a driver isn't loaded, the + * resources are not claimed; even if a driver is loaded, it + * may not request all resources or even the wrong one. We + * can neither trust the rest of the kernel nor ACPI/PNP and + * CRS parsing to get it right. Therefore, use several + * safeguards: + * + * - Do not auto-add resources if the CardBus bridge is on + * the PCI root bus + * + * - Avoid any I/O ports < 0x100. + * + * - On PCI-PCI bridges, only use resources which are set up + * exclusively for the secondary PCI bus: the risk of hitting + * system devices is quite low, as they usually aren't + * connected to the secondary PCI bus. */ if (s->cb_dev->bus->number == 0) return -EINVAL; -#endif + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { + res = s->cb_dev->bus->resource[i]; +#else pci_bus_for_each_resource(s->cb_dev->bus, res, i) { +#endif if (!res) continue; if (res->flags & IORESOURCE_IO) { + /* safeguard against the root resource, where the + * risk of hitting any other device would be too + * high */ if (res == &ioport_resource) continue; + dev_printk(KERN_INFO, &s->cb_dev->dev, "pcmcia: parent PCI bridge window: %pR\n", res); @@ -887,8 +980,12 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s) } if (res->flags & IORESOURCE_MEM) { + /* safeguard against the root resource, where the + * risk of hitting any other device would be too + * high */ if (res == &iomem_resource) continue; + dev_printk(KERN_INFO, &s->cb_dev->dev, "pcmcia: parent PCI bridge window: %pR\n", res); @@ -956,8 +1053,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) struct pccard_resource_ops pccard_nonstatic_ops = { .validate_mem = pcmcia_nonstatic_validate_mem, - .adjust_io_region = nonstatic_adjust_io_region, - .find_io = nonstatic_find_io_region, + .find_io = nonstatic_find_io, .find_mem = nonstatic_find_mem_region, .add_io = adjust_io, .add_mem = adjust_memory, diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 83ace277426c..424e576f3acb 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1303,13 +1303,6 @@ static int yenta_dev_suspend_noirq(struct device *dev) pci_read_config_dword(pdev, 17*4, &socket->saved_state[1]); pci_disable_device(pdev); - /* - * Some laptops (IBM T22) do not like us putting the Cardbus - * bridge into D3. At a guess, some other laptop will - * probably require this, so leave it commented out for now. - */ - /* pci_set_power_state(dev, 3); */ - return 0; } diff --git a/drivers/ps3/ps3-sys-manager.c b/drivers/ps3/ps3-sys-manager.c index 3cbaf1811bd0..d37c445f0eda 100644 --- a/drivers/ps3/ps3-sys-manager.c +++ b/drivers/ps3/ps3-sys-manager.c @@ -119,7 +119,7 @@ enum ps3_sys_manager_service_id { * enum ps3_sys_manager_attr - Notification attribute (bit position mask). * @PS3_SM_ATTR_POWER: Power button. * @PS3_SM_ATTR_RESET: Reset button, not available on retail console. - * @PS3_SM_ATTR_THERMAL: Sytem thermal alert. + * @PS3_SM_ATTR_THERMAL: System thermal alert. * @PS3_SM_ATTR_CONTROLLER: Remote controller event. * @PS3_SM_ATTR_ALL: Logical OR of all. * diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2b4e40d31190..51cf2bb37438 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1540,7 +1540,7 @@ EXPORT_SYMBOL_GPL(regulator_count_voltages); * Context: can sleep * * Returns a voltage that can be passed to @regulator_set_voltage(), - * zero if this selector code can't be used on this sytem, or a + * zero if this selector code can't be used on this system, or a * negative errno. */ int regulator_list_voltage(struct regulator *regulator, unsigned selector) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6a1303759432..50ac047cd136 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -620,6 +620,16 @@ config RTC_DRV_NUC900 comment "on-CPU RTC drivers" +config RTC_DRV_DAVINCI + tristate "TI DaVinci RTC" + depends on ARCH_DAVINCI_DM365 + help + If you say yes here you get support for the RTC on the + DaVinci platforms (DM365). + + This driver can also be built as a module. If so, the module + will be called rtc-davinci. + config RTC_DRV_OMAP tristate "TI OMAP1" depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 44ef194a9573..245311a1348f 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o +obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c new file mode 100644 index 000000000000..92a8f6cacda9 --- /dev/null +++ b/drivers/rtc/rtc-davinci.c @@ -0,0 +1,673 @@ +/* + * DaVinci Power Management and Real Time Clock Driver for TI platforms + * + * Copyright (C) 2009 Texas Instruments, Inc + * + * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +/* + * The DaVinci RTC is a simple RTC with the following + * Sec: 0 - 59 : BCD count + * Min: 0 - 59 : BCD count + * Hour: 0 - 23 : BCD count + * Day: 0 - 0x7FFF(32767) : Binary count ( Over 89 years ) + */ + +/* PRTC interface registers */ +#define DAVINCI_PRTCIF_PID 0x00 +#define PRTCIF_CTLR 0x04 +#define PRTCIF_LDATA 0x08 +#define PRTCIF_UDATA 0x0C +#define PRTCIF_INTEN 0x10 +#define PRTCIF_INTFLG 0x14 + +/* PRTCIF_CTLR bit fields */ +#define PRTCIF_CTLR_BUSY BIT(31) +#define PRTCIF_CTLR_SIZE BIT(25) +#define PRTCIF_CTLR_DIR BIT(24) +#define PRTCIF_CTLR_BENU_MSB BIT(23) +#define PRTCIF_CTLR_BENU_3RD_BYTE BIT(22) +#define PRTCIF_CTLR_BENU_2ND_BYTE BIT(21) +#define PRTCIF_CTLR_BENU_LSB BIT(20) +#define PRTCIF_CTLR_BENU_MASK (0x00F00000) +#define PRTCIF_CTLR_BENL_MSB BIT(19) +#define PRTCIF_CTLR_BENL_3RD_BYTE BIT(18) +#define PRTCIF_CTLR_BENL_2ND_BYTE BIT(17) +#define PRTCIF_CTLR_BENL_LSB BIT(16) +#define PRTCIF_CTLR_BENL_MASK (0x000F0000) + +/* PRTCIF_INTEN bit fields */ +#define PRTCIF_INTEN_RTCSS BIT(1) +#define PRTCIF_INTEN_RTCIF BIT(0) +#define PRTCIF_INTEN_MASK (PRTCIF_INTEN_RTCSS \ + | PRTCIF_INTEN_RTCIF) + +/* PRTCIF_INTFLG bit fields */ +#define PRTCIF_INTFLG_RTCSS BIT(1) +#define PRTCIF_INTFLG_RTCIF BIT(0) +#define PRTCIF_INTFLG_MASK (PRTCIF_INTFLG_RTCSS \ + | PRTCIF_INTFLG_RTCIF) + +/* PRTC subsystem registers */ +#define PRTCSS_RTC_INTC_EXTENA1 (0x0C) +#define PRTCSS_RTC_CTRL (0x10) +#define PRTCSS_RTC_WDT (0x11) +#define PRTCSS_RTC_TMR0 (0x12) +#define PRTCSS_RTC_TMR1 (0x13) +#define PRTCSS_RTC_CCTRL (0x14) +#define PRTCSS_RTC_SEC (0x15) +#define PRTCSS_RTC_MIN (0x16) +#define PRTCSS_RTC_HOUR (0x17) +#define PRTCSS_RTC_DAY0 (0x18) +#define PRTCSS_RTC_DAY1 (0x19) +#define PRTCSS_RTC_AMIN (0x1A) +#define PRTCSS_RTC_AHOUR (0x1B) +#define PRTCSS_RTC_ADAY0 (0x1C) +#define PRTCSS_RTC_ADAY1 (0x1D) +#define PRTCSS_RTC_CLKC_CNT (0x20) + +/* PRTCSS_RTC_INTC_EXTENA1 */ +#define PRTCSS_RTC_INTC_EXTENA1_MASK (0x07) + +/* PRTCSS_RTC_CTRL bit fields */ +#define PRTCSS_RTC_CTRL_WDTBUS BIT(7) +#define PRTCSS_RTC_CTRL_WEN BIT(6) +#define PRTCSS_RTC_CTRL_WDRT BIT(5) +#define PRTCSS_RTC_CTRL_WDTFLG BIT(4) +#define PRTCSS_RTC_CTRL_TE BIT(3) +#define PRTCSS_RTC_CTRL_TIEN BIT(2) +#define PRTCSS_RTC_CTRL_TMRFLG BIT(1) +#define PRTCSS_RTC_CTRL_TMMD BIT(0) + +/* PRTCSS_RTC_CCTRL bit fields */ +#define PRTCSS_RTC_CCTRL_CALBUSY BIT(7) +#define PRTCSS_RTC_CCTRL_DAEN BIT(5) +#define PRTCSS_RTC_CCTRL_HAEN BIT(4) +#define PRTCSS_RTC_CCTRL_MAEN BIT(3) +#define PRTCSS_RTC_CCTRL_ALMFLG BIT(2) +#define PRTCSS_RTC_CCTRL_AIEN BIT(1) +#define PRTCSS_RTC_CCTRL_CAEN BIT(0) + +static DEFINE_SPINLOCK(davinci_rtc_lock); + +struct davinci_rtc { + struct rtc_device *rtc; + void __iomem *base; + resource_size_t pbase; + size_t base_size; + int irq; +}; + +static inline void rtcif_write(struct davinci_rtc *davinci_rtc, + u32 val, u32 addr) +{ + writel(val, davinci_rtc->base + addr); +} + +static inline u32 rtcif_read(struct davinci_rtc *davinci_rtc, u32 addr) +{ + return readl(davinci_rtc->base + addr); +} + +static inline void rtcif_wait(struct davinci_rtc *davinci_rtc) +{ + while (rtcif_read(davinci_rtc, PRTCIF_CTLR) & PRTCIF_CTLR_BUSY) + cpu_relax(); +} + +static inline void rtcss_write(struct davinci_rtc *davinci_rtc, + unsigned long val, u8 addr) +{ + rtcif_wait(davinci_rtc); + + rtcif_write(davinci_rtc, PRTCIF_CTLR_BENL_LSB | addr, PRTCIF_CTLR); + rtcif_write(davinci_rtc, val, PRTCIF_LDATA); + + rtcif_wait(davinci_rtc); +} + +static inline u8 rtcss_read(struct davinci_rtc *davinci_rtc, u8 addr) +{ + rtcif_wait(davinci_rtc); + + rtcif_write(davinci_rtc, PRTCIF_CTLR_DIR | PRTCIF_CTLR_BENL_LSB | addr, + PRTCIF_CTLR); + + rtcif_wait(davinci_rtc); + + return rtcif_read(davinci_rtc, PRTCIF_LDATA); +} + +static inline void davinci_rtcss_calendar_wait(struct davinci_rtc *davinci_rtc) +{ + while (rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL) & + PRTCSS_RTC_CCTRL_CALBUSY) + cpu_relax(); +} + +static irqreturn_t davinci_rtc_interrupt(int irq, void *class_dev) +{ + struct davinci_rtc *davinci_rtc = class_dev; + unsigned long events = 0; + u32 irq_flg; + u8 alm_irq, tmr_irq; + u8 rtc_ctrl, rtc_cctrl; + int ret = IRQ_NONE; + + irq_flg = rtcif_read(davinci_rtc, PRTCIF_INTFLG) & + PRTCIF_INTFLG_RTCSS; + + alm_irq = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL) & + PRTCSS_RTC_CCTRL_ALMFLG; + + tmr_irq = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL) & + PRTCSS_RTC_CTRL_TMRFLG; + + if (irq_flg) { + if (alm_irq) { + events |= RTC_IRQF | RTC_AF; + rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL); + rtc_cctrl |= PRTCSS_RTC_CCTRL_ALMFLG; + rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL); + } else if (tmr_irq) { + events |= RTC_IRQF | RTC_PF; + rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL); + rtc_ctrl |= PRTCSS_RTC_CTRL_TMRFLG; + rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL); + } + + rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS, + PRTCIF_INTFLG); + rtc_update_irq(davinci_rtc->rtc, 1, events); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int +davinci_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + u8 rtc_ctrl; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL); + + switch (cmd) { + case RTC_WIE_ON: + rtc_ctrl |= PRTCSS_RTC_CTRL_WEN | PRTCSS_RTC_CTRL_WDTFLG; + break; + case RTC_WIE_OFF: + rtc_ctrl &= ~PRTCSS_RTC_CTRL_WEN; + break; + case RTC_UIE_OFF: + case RTC_UIE_ON: + ret = -ENOTTY; + break; + default: + ret = -ENOIOCTLCMD; + } + + rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return ret; +} + +static int convertfromdays(u16 days, struct rtc_time *tm) +{ + int tmp_days, year, mon; + + for (year = 2000;; year++) { + tmp_days = rtc_year_days(1, 12, year); + if (days >= tmp_days) + days -= tmp_days; + else { + for (mon = 0;; mon++) { + tmp_days = rtc_month_days(mon, year); + if (days >= tmp_days) { + days -= tmp_days; + } else { + tm->tm_year = year - 1900; + tm->tm_mon = mon; + tm->tm_mday = days + 1; + break; + } + } + break; + } + } + return 0; +} + +static int convert2days(u16 *days, struct rtc_time *tm) +{ + int i; + *days = 0; + + /* epoch == 1900 */ + if (tm->tm_year < 100 || tm->tm_year > 199) + return -EINVAL; + + for (i = 2000; i < 1900 + tm->tm_year; i++) + *days += rtc_year_days(1, 12, i); + + *days += rtc_year_days(tm->tm_mday, tm->tm_mon, 1900 + tm->tm_year); + + return 0; +} + +static int davinci_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + u16 days = 0; + u8 day0, day1; + unsigned long flags; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_calendar_wait(davinci_rtc); + tm->tm_sec = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_SEC)); + + davinci_rtcss_calendar_wait(davinci_rtc); + tm->tm_min = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_MIN)); + + davinci_rtcss_calendar_wait(davinci_rtc); + tm->tm_hour = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_HOUR)); + + davinci_rtcss_calendar_wait(davinci_rtc); + day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY0); + + davinci_rtcss_calendar_wait(davinci_rtc); + day1 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY1); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + days |= day1; + days <<= 8; + days |= day0; + + if (convertfromdays(days, tm) < 0) + return -EINVAL; + + return 0; +} + +static int davinci_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + u16 days; + u8 rtc_cctrl; + unsigned long flags; + + if (convert2days(&days, tm) < 0) + return -EINVAL; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, bin2bcd(tm->tm_sec), PRTCSS_RTC_SEC); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, bin2bcd(tm->tm_min), PRTCSS_RTC_MIN); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, bin2bcd(tm->tm_hour), PRTCSS_RTC_HOUR); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, days & 0xFF, PRTCSS_RTC_DAY0); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, PRTCSS_RTC_DAY1); + + rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL); + rtc_cctrl |= PRTCSS_RTC_CCTRL_CAEN; + rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static int davinci_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + unsigned long flags; + u8 rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL); + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + if (enabled) + rtc_cctrl |= PRTCSS_RTC_CCTRL_DAEN | + PRTCSS_RTC_CCTRL_HAEN | + PRTCSS_RTC_CCTRL_MAEN | + PRTCSS_RTC_CCTRL_ALMFLG | + PRTCSS_RTC_CCTRL_AIEN; + else + rtc_cctrl &= ~PRTCSS_RTC_CCTRL_AIEN; + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static int davinci_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + u16 days = 0; + u8 day0, day1; + unsigned long flags; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_calendar_wait(davinci_rtc); + alm->time.tm_min = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_AMIN)); + + davinci_rtcss_calendar_wait(davinci_rtc); + alm->time.tm_hour = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_AHOUR)); + + davinci_rtcss_calendar_wait(davinci_rtc); + day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_ADAY0); + + davinci_rtcss_calendar_wait(davinci_rtc); + day1 = rtcss_read(davinci_rtc, PRTCSS_RTC_ADAY1); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + days |= day1; + days <<= 8; + days |= day0; + + if (convertfromdays(days, &alm->time) < 0) + return -EINVAL; + + alm->pending = !!(rtcss_read(davinci_rtc, + PRTCSS_RTC_CCTRL) & + PRTCSS_RTC_CCTRL_AIEN); + alm->enabled = alm->pending && device_may_wakeup(dev); + + return 0; +} + +static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + unsigned long flags; + u16 days; + + if (alm->time.tm_mday <= 0 && alm->time.tm_mon < 0 + && alm->time.tm_year < 0) { + struct rtc_time tm; + unsigned long now, then; + + davinci_rtc_read_time(dev, &tm); + rtc_tm_to_time(&tm, &now); + + alm->time.tm_mday = tm.tm_mday; + alm->time.tm_mon = tm.tm_mon; + alm->time.tm_year = tm.tm_year; + rtc_tm_to_time(&alm->time, &then); + + if (then < now) { + rtc_time_to_tm(now + 24 * 60 * 60, &tm); + alm->time.tm_mday = tm.tm_mday; + alm->time.tm_mon = tm.tm_mon; + alm->time.tm_year = tm.tm_year; + } + } + + if (convert2days(&days, &alm->time) < 0) + return -EINVAL; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_min), PRTCSS_RTC_AMIN); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_hour), PRTCSS_RTC_AHOUR); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, days & 0xFF, PRTCSS_RTC_ADAY0); + + davinci_rtcss_calendar_wait(davinci_rtc); + rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, PRTCSS_RTC_ADAY1); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static int davinci_rtc_irq_set_state(struct device *dev, int enabled) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + unsigned long flags; + u8 rtc_ctrl; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL); + + if (enabled) { + while (rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL) + & PRTCSS_RTC_CTRL_WDTBUS) + cpu_relax(); + + rtc_ctrl |= PRTCSS_RTC_CTRL_TE; + rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL); + + rtcss_write(davinci_rtc, 0x0, PRTCSS_RTC_CLKC_CNT); + + rtc_ctrl |= PRTCSS_RTC_CTRL_TIEN | + PRTCSS_RTC_CTRL_TMMD | + PRTCSS_RTC_CTRL_TMRFLG; + } else + rtc_ctrl &= ~PRTCSS_RTC_CTRL_TIEN; + + rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static int davinci_rtc_irq_set_freq(struct device *dev, int freq) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + unsigned long flags; + u16 tmr_counter = (0x8000 >> (ffs(freq) - 1)); + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + rtcss_write(davinci_rtc, tmr_counter & 0xFF, PRTCSS_RTC_TMR0); + rtcss_write(davinci_rtc, (tmr_counter & 0xFF00) >> 8, PRTCSS_RTC_TMR1); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static struct rtc_class_ops davinci_rtc_ops = { + .ioctl = davinci_rtc_ioctl, + .read_time = davinci_rtc_read_time, + .set_time = davinci_rtc_set_time, + .alarm_irq_enable = davinci_rtc_alarm_irq_enable, + .read_alarm = davinci_rtc_read_alarm, + .set_alarm = davinci_rtc_set_alarm, + .irq_set_state = davinci_rtc_irq_set_state, + .irq_set_freq = davinci_rtc_irq_set_freq, +}; + +static int __init davinci_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct davinci_rtc *davinci_rtc; + struct resource *res, *mem; + int ret = 0; + + davinci_rtc = kzalloc(sizeof(struct davinci_rtc), GFP_KERNEL); + if (!davinci_rtc) { + dev_dbg(dev, "could not allocate memory for private data\n"); + return -ENOMEM; + } + + davinci_rtc->irq = platform_get_irq(pdev, 0); + if (davinci_rtc->irq < 0) { + dev_err(dev, "no RTC irq\n"); + ret = davinci_rtc->irq; + goto fail1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no mem resource\n"); + ret = -EINVAL; + goto fail1; + } + + davinci_rtc->pbase = res->start; + davinci_rtc->base_size = resource_size(res); + + mem = request_mem_region(davinci_rtc->pbase, davinci_rtc->base_size, + pdev->name); + if (!mem) { + dev_err(dev, "RTC registers at %08x are not free\n", + davinci_rtc->pbase); + ret = -EBUSY; + goto fail1; + } + + davinci_rtc->base = ioremap(davinci_rtc->pbase, davinci_rtc->base_size); + if (!davinci_rtc->base) { + dev_err(dev, "unable to ioremap MEM resource\n"); + ret = -ENOMEM; + goto fail2; + } + + davinci_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &davinci_rtc_ops, THIS_MODULE); + if (IS_ERR(davinci_rtc->rtc)) { + dev_err(dev, "unable to register RTC device, err %ld\n", + PTR_ERR(davinci_rtc->rtc)); + goto fail3; + } + + rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS, PRTCIF_INTFLG); + rtcif_write(davinci_rtc, 0, PRTCIF_INTEN); + rtcss_write(davinci_rtc, 0, PRTCSS_RTC_INTC_EXTENA1); + + rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CTRL); + rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CCTRL); + + ret = request_irq(davinci_rtc->irq, davinci_rtc_interrupt, + IRQF_DISABLED, "davinci_rtc", davinci_rtc); + if (ret < 0) { + dev_err(dev, "unable to register davinci RTC interrupt\n"); + goto fail4; + } + + /* Enable interrupts */ + rtcif_write(davinci_rtc, PRTCIF_INTEN_RTCSS, PRTCIF_INTEN); + rtcss_write(davinci_rtc, PRTCSS_RTC_INTC_EXTENA1_MASK, + PRTCSS_RTC_INTC_EXTENA1); + + rtcss_write(davinci_rtc, PRTCSS_RTC_CCTRL_CAEN, PRTCSS_RTC_CCTRL); + + platform_set_drvdata(pdev, davinci_rtc); + + device_init_wakeup(&pdev->dev, 0); + + return 0; + +fail4: + rtc_device_unregister(davinci_rtc->rtc); +fail3: + iounmap(davinci_rtc->base); +fail2: + release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size); +fail1: + kfree(davinci_rtc); + + return ret; +} + +static int __devexit davinci_rtc_remove(struct platform_device *pdev) +{ + struct davinci_rtc *davinci_rtc = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + + rtcif_write(davinci_rtc, 0, PRTCIF_INTEN); + + free_irq(davinci_rtc->irq, davinci_rtc); + + rtc_device_unregister(davinci_rtc->rtc); + + iounmap(davinci_rtc->base); + release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size); + + platform_set_drvdata(pdev, NULL); + + kfree(davinci_rtc); + + return 0; +} + +static struct platform_driver davinci_rtc_driver = { + .probe = davinci_rtc_probe, + .remove = __devexit_p(davinci_rtc_remove), + .driver = { + .name = "rtc_davinci", + .owner = THIS_MODULE, + }, +}; + +static int __init rtc_init(void) +{ + return platform_driver_probe(&davinci_rtc_driver, davinci_rtc_probe); +} +module_init(rtc_init); + +static void __exit rtc_exit(void) +{ + platform_driver_unregister(&davinci_rtc_driver); +} +module_exit(rtc_exit); + +MODULE_AUTHOR("Miguel Aguilar <miguel.aguilar@ridgerun.com>"); +MODULE_DESCRIPTION("Texas Instruments DaVinci PRTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index c9522f3bc21c..9718aaaa8215 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -1,8 +1,8 @@ /* * An I2C driver for the Epson RX8581 RTC * - * Author: Martyn Welch <martyn.welch@gefanuc.com> - * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc. + * Author: Martyn Welch <martyn.welch@ge.com> + * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. * * 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 @@ -272,7 +272,7 @@ static void __exit rx8581_exit(void) i2c_del_driver(&rx8581_driver); } -MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com>"); +MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>"); MODULE_DESCRIPTION("Epson RX-8581 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c index 875ba099e7a5..b53a00198dbe 100644 --- a/drivers/rtc/rtc-stk17ta8.c +++ b/drivers/rtc/rtc-stk17ta8.c @@ -1,7 +1,7 @@ /* * A RTC driver for the Simtek STK17TA8 * - * By Thomas Hommel <thomas.hommel@gefanuc.com> + * By Thomas Hommel <thomas.hommel@ge.com> * * Based on the DS1553 driver from * Atsushi Nemoto <anemo@mba.ocn.ne.jp> @@ -382,7 +382,7 @@ static __exit void stk17ta8_exit(void) module_init(stk17ta8_init); module_exit(stk17ta8_exit); -MODULE_AUTHOR("Thomas Hommel <thomas.hommel@gefanuc.com>"); +MODULE_AUTHOR("Thomas Hommel <thomas.hommel@ge.com>"); MODULE_DESCRIPTION("Simtek STK17TA8 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index fa2339cb1681..0e86247d791e 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -65,6 +65,7 @@ static void dasd_device_tasklet(struct dasd_device *); static void dasd_block_tasklet(struct dasd_block *); static void do_kick_device(struct work_struct *); static void do_restore_device(struct work_struct *); +static void do_reload_device(struct work_struct *); static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); static void dasd_device_timeout(unsigned long); static void dasd_block_timeout(unsigned long); @@ -115,6 +116,7 @@ struct dasd_device *dasd_alloc_device(void) device->timer.data = (unsigned long) device; INIT_WORK(&device->kick_work, do_kick_device); INIT_WORK(&device->restore_device, do_restore_device); + INIT_WORK(&device->reload_device, do_reload_device); device->state = DASD_STATE_NEW; device->target = DASD_STATE_NEW; mutex_init(&device->state_mutex); @@ -521,6 +523,26 @@ void dasd_kick_device(struct dasd_device *device) } /* + * dasd_reload_device will schedule a call do do_reload_device to the kernel + * event daemon. + */ +static void do_reload_device(struct work_struct *work) +{ + struct dasd_device *device = container_of(work, struct dasd_device, + reload_device); + device->discipline->reload(device); + dasd_put_device(device); +} + +void dasd_reload_device(struct dasd_device *device) +{ + dasd_get_device(device); + /* queue call to dasd_reload_device to the kernel event daemon. */ + schedule_work(&device->reload_device); +} +EXPORT_SYMBOL(dasd_reload_device); + +/* * dasd_restore_device will schedule a call do do_restore_device to the kernel * event daemon. */ diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 6632649dd6aa..85bfd8794856 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -1418,9 +1418,29 @@ static struct dasd_ccw_req *dasd_3990_erp_inspect_alias( struct dasd_ccw_req *erp) { struct dasd_ccw_req *cqr = erp->refers; + char *sense; if (cqr->block && (cqr->block->base != cqr->startdev)) { + + sense = dasd_get_sense(&erp->refers->irb); + /* + * dynamic pav may have changed base alias mapping + */ + if (!test_bit(DASD_FLAG_OFFLINE, &cqr->startdev->flags) && sense + && (sense[0] == 0x10) && (sense[7] == 0x0F) + && (sense[8] == 0x67)) { + /* + * remove device from alias handling to prevent new + * requests from being scheduled on the + * wrong alias device + */ + dasd_alias_remove_device(cqr->startdev); + + /* schedule worker to reload device */ + dasd_reload_device(cqr->startdev); + } + if (cqr->startdev->features & DASD_FEATURE_ERPLOG) { DBF_DEV_EVENT(DBF_ERR, cqr->startdev, "ERP on alias device for request %p," diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 8c4814258e93..4155805dcdff 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -190,20 +190,21 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) struct alias_server *server, *newserver; struct alias_lcu *lcu, *newlcu; int is_lcu_known; - struct dasd_uid *uid; + struct dasd_uid uid; private = (struct dasd_eckd_private *) device->private; - uid = &private->uid; + + device->discipline->get_uid(device, &uid); spin_lock_irqsave(&aliastree.lock, flags); is_lcu_known = 1; - server = _find_server(uid); + server = _find_server(&uid); if (!server) { spin_unlock_irqrestore(&aliastree.lock, flags); - newserver = _allocate_server(uid); + newserver = _allocate_server(&uid); if (IS_ERR(newserver)) return PTR_ERR(newserver); spin_lock_irqsave(&aliastree.lock, flags); - server = _find_server(uid); + server = _find_server(&uid); if (!server) { list_add(&newserver->server, &aliastree.serverlist); server = newserver; @@ -214,14 +215,14 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) } } - lcu = _find_lcu(server, uid); + lcu = _find_lcu(server, &uid); if (!lcu) { spin_unlock_irqrestore(&aliastree.lock, flags); - newlcu = _allocate_lcu(uid); + newlcu = _allocate_lcu(&uid); if (IS_ERR(newlcu)) return PTR_ERR(newlcu); spin_lock_irqsave(&aliastree.lock, flags); - lcu = _find_lcu(server, uid); + lcu = _find_lcu(server, &uid); if (!lcu) { list_add(&newlcu->lcu, &server->lculist); lcu = newlcu; @@ -256,20 +257,20 @@ void dasd_alias_lcu_setup_complete(struct dasd_device *device) unsigned long flags; struct alias_server *server; struct alias_lcu *lcu; - struct dasd_uid *uid; + struct dasd_uid uid; private = (struct dasd_eckd_private *) device->private; - uid = &private->uid; + device->discipline->get_uid(device, &uid); lcu = NULL; spin_lock_irqsave(&aliastree.lock, flags); - server = _find_server(uid); + server = _find_server(&uid); if (server) - lcu = _find_lcu(server, uid); + lcu = _find_lcu(server, &uid); spin_unlock_irqrestore(&aliastree.lock, flags); if (!lcu) { DBF_EVENT_DEVID(DBF_ERR, device->cdev, "could not find lcu for %04x %02x", - uid->ssid, uid->real_unit_addr); + uid.ssid, uid.real_unit_addr); WARN_ON(1); return; } @@ -282,20 +283,20 @@ void dasd_alias_wait_for_lcu_setup(struct dasd_device *device) unsigned long flags; struct alias_server *server; struct alias_lcu *lcu; - struct dasd_uid *uid; + struct dasd_uid uid; private = (struct dasd_eckd_private *) device->private; - uid = &private->uid; + device->discipline->get_uid(device, &uid); lcu = NULL; spin_lock_irqsave(&aliastree.lock, flags); - server = _find_server(uid); + server = _find_server(&uid); if (server) - lcu = _find_lcu(server, uid); + lcu = _find_lcu(server, &uid); spin_unlock_irqrestore(&aliastree.lock, flags); if (!lcu) { DBF_EVENT_DEVID(DBF_ERR, device->cdev, "could not find lcu for %04x %02x", - uid->ssid, uid->real_unit_addr); + uid.ssid, uid.real_unit_addr); WARN_ON(1); return; } @@ -314,9 +315,11 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) struct alias_lcu *lcu; struct alias_server *server; int was_pending; + struct dasd_uid uid; private = (struct dasd_eckd_private *) device->private; lcu = private->lcu; + device->discipline->get_uid(device, &uid); spin_lock_irqsave(&lcu->lock, flags); list_del_init(&device->alias_list); /* make sure that the workers don't use this device */ @@ -353,7 +356,7 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) _schedule_lcu_update(lcu, NULL); spin_unlock(&lcu->lock); } - server = _find_server(&private->uid); + server = _find_server(&uid); if (server && list_empty(&server->lculist)) { list_del(&server->server); _free_server(server); @@ -366,19 +369,30 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) * in the lcu is up to date and will update the device uid before * adding it to a pav group. */ + static int _add_device_to_lcu(struct alias_lcu *lcu, - struct dasd_device *device) + struct dasd_device *device, + struct dasd_device *pos) { struct dasd_eckd_private *private; struct alias_pav_group *group; - struct dasd_uid *uid; + struct dasd_uid uid; + unsigned long flags; private = (struct dasd_eckd_private *) device->private; - uid = &private->uid; - uid->type = lcu->uac->unit[uid->real_unit_addr].ua_type; - uid->base_unit_addr = lcu->uac->unit[uid->real_unit_addr].base_ua; - dasd_set_uid(device->cdev, &private->uid); + + /* only lock if not already locked */ + if (device != pos) + spin_lock_irqsave_nested(get_ccwdev_lock(device->cdev), flags, + CDEV_NESTED_SECOND); + private->uid.type = lcu->uac->unit[private->uid.real_unit_addr].ua_type; + private->uid.base_unit_addr = + lcu->uac->unit[private->uid.real_unit_addr].base_ua; + uid = private->uid; + + if (device != pos) + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); /* if we have no PAV anyway, we don't need to bother with PAV groups */ if (lcu->pav == NO_PAV) { @@ -386,25 +400,25 @@ static int _add_device_to_lcu(struct alias_lcu *lcu, return 0; } - group = _find_group(lcu, uid); + group = _find_group(lcu, &uid); if (!group) { group = kzalloc(sizeof(*group), GFP_ATOMIC); if (!group) return -ENOMEM; - memcpy(group->uid.vendor, uid->vendor, sizeof(uid->vendor)); - memcpy(group->uid.serial, uid->serial, sizeof(uid->serial)); - group->uid.ssid = uid->ssid; - if (uid->type == UA_BASE_DEVICE) - group->uid.base_unit_addr = uid->real_unit_addr; + memcpy(group->uid.vendor, uid.vendor, sizeof(uid.vendor)); + memcpy(group->uid.serial, uid.serial, sizeof(uid.serial)); + group->uid.ssid = uid.ssid; + if (uid.type == UA_BASE_DEVICE) + group->uid.base_unit_addr = uid.real_unit_addr; else - group->uid.base_unit_addr = uid->base_unit_addr; - memcpy(group->uid.vduit, uid->vduit, sizeof(uid->vduit)); + group->uid.base_unit_addr = uid.base_unit_addr; + memcpy(group->uid.vduit, uid.vduit, sizeof(uid.vduit)); INIT_LIST_HEAD(&group->group); INIT_LIST_HEAD(&group->baselist); INIT_LIST_HEAD(&group->aliaslist); list_add(&group->group, &lcu->grouplist); } - if (uid->type == UA_BASE_DEVICE) + if (uid.type == UA_BASE_DEVICE) list_move(&device->alias_list, &group->baselist); else list_move(&device->alias_list, &group->aliaslist); @@ -525,7 +539,10 @@ static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu) if (rc) return rc; - spin_lock_irqsave(&lcu->lock, flags); + /* need to take cdev lock before lcu lock */ + spin_lock_irqsave_nested(get_ccwdev_lock(refdev->cdev), flags, + CDEV_NESTED_FIRST); + spin_lock(&lcu->lock); lcu->pav = NO_PAV; for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) { switch (lcu->uac->unit[i].ua_type) { @@ -542,9 +559,10 @@ static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu) list_for_each_entry_safe(device, tempdev, &lcu->active_devices, alias_list) { - _add_device_to_lcu(lcu, device); + _add_device_to_lcu(lcu, device, refdev); } - spin_unlock_irqrestore(&lcu->lock, flags); + spin_unlock(&lcu->lock); + spin_unlock_irqrestore(get_ccwdev_lock(refdev->cdev), flags); return 0; } @@ -628,9 +646,12 @@ int dasd_alias_add_device(struct dasd_device *device) private = (struct dasd_eckd_private *) device->private; lcu = private->lcu; rc = 0; - spin_lock_irqsave(&lcu->lock, flags); + + /* need to take cdev lock before lcu lock */ + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + spin_lock(&lcu->lock); if (!(lcu->flags & UPDATE_PENDING)) { - rc = _add_device_to_lcu(lcu, device); + rc = _add_device_to_lcu(lcu, device, device); if (rc) lcu->flags |= UPDATE_PENDING; } @@ -638,10 +659,19 @@ int dasd_alias_add_device(struct dasd_device *device) list_move(&device->alias_list, &lcu->active_devices); _schedule_lcu_update(lcu, device); } - spin_unlock_irqrestore(&lcu->lock, flags); + spin_unlock(&lcu->lock); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); return rc; } +int dasd_alias_update_add_device(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + private = (struct dasd_eckd_private *) device->private; + private->lcu->flags |= UPDATE_PENDING; + return dasd_alias_add_device(device); +} + int dasd_alias_remove_device(struct dasd_device *device) { struct dasd_eckd_private *private; @@ -740,19 +770,30 @@ static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu) struct alias_pav_group *pavgroup; struct dasd_device *device; struct dasd_eckd_private *private; + unsigned long flags; /* active and inactive list can contain alias as well as base devices */ list_for_each_entry(device, &lcu->active_devices, alias_list) { private = (struct dasd_eckd_private *) device->private; - if (private->uid.type != UA_BASE_DEVICE) + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + if (private->uid.type != UA_BASE_DEVICE) { + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), + flags); continue; + } + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); dasd_schedule_block_bh(device->block); dasd_schedule_device_bh(device); } list_for_each_entry(device, &lcu->inactive_devices, alias_list) { private = (struct dasd_eckd_private *) device->private; - if (private->uid.type != UA_BASE_DEVICE) + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + if (private->uid.type != UA_BASE_DEVICE) { + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), + flags); continue; + } + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); dasd_schedule_block_bh(device->block); dasd_schedule_device_bh(device); } diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index eff9c812c5c2..34d51dd4c539 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -49,7 +49,6 @@ struct dasd_devmap { unsigned int devindex; unsigned short features; struct dasd_device *device; - struct dasd_uid uid; }; /* @@ -936,42 +935,46 @@ dasd_device_status_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(status, 0444, dasd_device_status_show, NULL); -static ssize_t -dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t dasd_alias_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct dasd_devmap *devmap; - int alias; + struct dasd_device *device; + struct dasd_uid uid; - devmap = dasd_find_busid(dev_name(dev)); - spin_lock(&dasd_devmap_lock); - if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) { - spin_unlock(&dasd_devmap_lock); + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) return sprintf(buf, "0\n"); + + if (device->discipline && device->discipline->get_uid && + !device->discipline->get_uid(device, &uid)) { + if (uid.type == UA_BASE_PAV_ALIAS || + uid.type == UA_HYPER_PAV_ALIAS) + return sprintf(buf, "1\n"); } - if (devmap->uid.type == UA_BASE_PAV_ALIAS || - devmap->uid.type == UA_HYPER_PAV_ALIAS) - alias = 1; - else - alias = 0; - spin_unlock(&dasd_devmap_lock); - return sprintf(buf, alias ? "1\n" : "0\n"); + dasd_put_device(device); + + return sprintf(buf, "0\n"); } static DEVICE_ATTR(alias, 0444, dasd_alias_show, NULL); -static ssize_t -dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t dasd_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct dasd_devmap *devmap; + struct dasd_device *device; + struct dasd_uid uid; char *vendor; - devmap = dasd_find_busid(dev_name(dev)); - spin_lock(&dasd_devmap_lock); - if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0) - vendor = devmap->uid.vendor; - else - vendor = ""; - spin_unlock(&dasd_devmap_lock); + device = dasd_device_from_cdev(to_ccwdev(dev)); + vendor = ""; + if (IS_ERR(device)) + return snprintf(buf, PAGE_SIZE, "%s\n", vendor); + + if (device->discipline && device->discipline->get_uid && + !device->discipline->get_uid(device, &uid)) + vendor = uid.vendor; + + dasd_put_device(device); return snprintf(buf, PAGE_SIZE, "%s\n", vendor); } @@ -985,48 +988,51 @@ static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL); static ssize_t dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dasd_devmap *devmap; + struct dasd_device *device; + struct dasd_uid uid; char uid_string[UID_STRLEN]; char ua_string[3]; - struct dasd_uid *uid; - devmap = dasd_find_busid(dev_name(dev)); - spin_lock(&dasd_devmap_lock); - if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) { - spin_unlock(&dasd_devmap_lock); - return sprintf(buf, "\n"); - } - uid = &devmap->uid; - switch (uid->type) { - case UA_BASE_DEVICE: - sprintf(ua_string, "%02x", uid->real_unit_addr); - break; - case UA_BASE_PAV_ALIAS: - sprintf(ua_string, "%02x", uid->base_unit_addr); - break; - case UA_HYPER_PAV_ALIAS: - sprintf(ua_string, "xx"); - break; - default: - /* should not happen, treat like base device */ - sprintf(ua_string, "%02x", uid->real_unit_addr); - break; + device = dasd_device_from_cdev(to_ccwdev(dev)); + uid_string[0] = 0; + if (IS_ERR(device)) + return snprintf(buf, PAGE_SIZE, "%s\n", uid_string); + + if (device->discipline && device->discipline->get_uid && + !device->discipline->get_uid(device, &uid)) { + switch (uid.type) { + case UA_BASE_DEVICE: + snprintf(ua_string, sizeof(ua_string), "%02x", + uid.real_unit_addr); + break; + case UA_BASE_PAV_ALIAS: + snprintf(ua_string, sizeof(ua_string), "%02x", + uid.base_unit_addr); + break; + case UA_HYPER_PAV_ALIAS: + snprintf(ua_string, sizeof(ua_string), "xx"); + break; + default: + /* should not happen, treat like base device */ + snprintf(ua_string, sizeof(ua_string), "%02x", + uid.real_unit_addr); + break; + } + + if (strlen(uid.vduit) > 0) + snprintf(uid_string, sizeof(uid_string), + "%s.%s.%04x.%s.%s", + uid.vendor, uid.serial, uid.ssid, ua_string, + uid.vduit); + else + snprintf(uid_string, sizeof(uid_string), + "%s.%s.%04x.%s", + uid.vendor, uid.serial, uid.ssid, ua_string); } - if (strlen(uid->vduit) > 0) - snprintf(uid_string, sizeof(uid_string), - "%s.%s.%04x.%s.%s", - uid->vendor, uid->serial, - uid->ssid, ua_string, - uid->vduit); - else - snprintf(uid_string, sizeof(uid_string), - "%s.%s.%04x.%s", - uid->vendor, uid->serial, - uid->ssid, ua_string); - spin_unlock(&dasd_devmap_lock); + dasd_put_device(device); + return snprintf(buf, PAGE_SIZE, "%s\n", uid_string); } - static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL); /* @@ -1094,50 +1100,6 @@ static struct attribute_group dasd_attr_group = { }; /* - * Return copy of the device unique identifier. - */ -int -dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid) -{ - struct dasd_devmap *devmap; - - devmap = dasd_find_busid(dev_name(&cdev->dev)); - if (IS_ERR(devmap)) - return PTR_ERR(devmap); - spin_lock(&dasd_devmap_lock); - *uid = devmap->uid; - spin_unlock(&dasd_devmap_lock); - return 0; -} -EXPORT_SYMBOL_GPL(dasd_get_uid); - -/* - * Register the given device unique identifier into devmap struct. - * In addition check if the related storage server subsystem ID is already - * contained in the dasd_server_ssid_list. If subsystem ID is not contained, - * create new entry. - * Return 0 if server was already in serverlist, - * 1 if the server was added successful - * <0 in case of error. - */ -int -dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid) -{ - struct dasd_devmap *devmap; - - devmap = dasd_find_busid(dev_name(&cdev->dev)); - if (IS_ERR(devmap)) - return PTR_ERR(devmap); - - spin_lock(&dasd_devmap_lock); - devmap->uid = *uid; - spin_unlock(&dasd_devmap_lock); - - return 0; -} -EXPORT_SYMBOL_GPL(dasd_set_uid); - -/* * Return value of the specified feature. */ int diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 0cb233116855..5b1cd8d6e971 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -692,18 +692,20 @@ dasd_eckd_cdl_reclen(int recid) /* * Generate device unique id that specifies the physical device. */ -static int dasd_eckd_generate_uid(struct dasd_device *device, - struct dasd_uid *uid) +static int dasd_eckd_generate_uid(struct dasd_device *device) { struct dasd_eckd_private *private; + struct dasd_uid *uid; int count; + unsigned long flags; private = (struct dasd_eckd_private *) device->private; if (!private) return -ENODEV; if (!private->ned || !private->gneq) return -ENODEV; - + uid = &private->uid; + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); memset(uid, 0, sizeof(struct dasd_uid)); memcpy(uid->vendor, private->ned->HDA_manufacturer, sizeof(uid->vendor) - 1); @@ -726,9 +728,25 @@ static int dasd_eckd_generate_uid(struct dasd_device *device, private->vdsneq->uit[count]); } } + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); return 0; } +static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid) +{ + struct dasd_eckd_private *private; + unsigned long flags; + + if (device->private) { + private = (struct dasd_eckd_private *)device->private; + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + *uid = private->uid; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + return 0; + } + return -EINVAL; +} + static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, void *rcd_buffer, struct ciw *ciw, __u8 lpm) @@ -1088,6 +1106,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) { struct dasd_eckd_private *private; struct dasd_block *block; + struct dasd_uid temp_uid; int is_known, rc; int readonly; @@ -1124,13 +1143,13 @@ dasd_eckd_check_characteristics(struct dasd_device *device) if (rc) goto out_err1; - /* Generate device unique id and register in devmap */ - rc = dasd_eckd_generate_uid(device, &private->uid); + /* Generate device unique id */ + rc = dasd_eckd_generate_uid(device); if (rc) goto out_err1; - dasd_set_uid(device->cdev, &private->uid); - if (private->uid.type == UA_BASE_DEVICE) { + dasd_eckd_get_uid(device, &temp_uid); + if (temp_uid.type == UA_BASE_DEVICE) { block = dasd_alloc_block(); if (IS_ERR(block)) { DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", @@ -1451,6 +1470,7 @@ static int dasd_eckd_ready_to_online(struct dasd_device *device) static int dasd_eckd_online_to_ready(struct dasd_device *device) { + cancel_work_sync(&device->reload_device); return dasd_alias_remove_device(device); }; @@ -1709,10 +1729,27 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, { char mask; char *sense = NULL; + struct dasd_eckd_private *private; + private = (struct dasd_eckd_private *) device->private; /* first of all check for state change pending interrupt */ mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; if ((scsw_dstat(&irb->scsw) & mask) == mask) { + /* for alias only and not in offline processing*/ + if (!device->block && private->lcu && + !test_bit(DASD_FLAG_OFFLINE, &device->flags)) { + /* + * the state change could be caused by an alias + * reassignment remove device from alias handling + * to prevent new requests from being scheduled on + * the wrong alias device + */ + dasd_alias_remove_device(device); + + /* schedule worker to reload device */ + dasd_reload_device(device); + } + dasd_generic_handle_state_change(device); return; } @@ -3259,7 +3296,7 @@ static void dasd_eckd_dump_sense(struct dasd_device *device, dasd_eckd_dump_sense_ccw(device, req, irb); } -int dasd_eckd_pm_freeze(struct dasd_device *device) +static int dasd_eckd_pm_freeze(struct dasd_device *device) { /* * the device should be disconnected from our LCU structure @@ -3272,7 +3309,7 @@ int dasd_eckd_pm_freeze(struct dasd_device *device) return 0; } -int dasd_eckd_restore_device(struct dasd_device *device) +static int dasd_eckd_restore_device(struct dasd_device *device) { struct dasd_eckd_private *private; struct dasd_eckd_characteristics temp_rdc_data; @@ -3287,15 +3324,16 @@ int dasd_eckd_restore_device(struct dasd_device *device) if (rc) goto out_err; - /* Generate device unique id and register in devmap */ - rc = dasd_eckd_generate_uid(device, &private->uid); - dasd_get_uid(device->cdev, &temp_uid); + dasd_eckd_get_uid(device, &temp_uid); + /* Generate device unique id */ + rc = dasd_eckd_generate_uid(device); + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0) dev_err(&device->cdev->dev, "The UID of the DASD has " "changed\n"); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); if (rc) goto out_err; - dasd_set_uid(device->cdev, &private->uid); /* register lcu with alias handling, enable PAV if this is a new lcu */ is_known = dasd_alias_make_device_known_to_lcu(device); @@ -3336,6 +3374,56 @@ out_err: return -1; } +static int dasd_eckd_reload_device(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + int rc, old_base; + char print_uid[60]; + struct dasd_uid uid; + unsigned long flags; + + private = (struct dasd_eckd_private *) device->private; + + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + old_base = private->uid.base_unit_addr; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + + /* Read Configuration Data */ + rc = dasd_eckd_read_conf(device); + if (rc) + goto out_err; + + rc = dasd_eckd_generate_uid(device); + if (rc) + goto out_err; + /* + * update unit address configuration and + * add device to alias management + */ + dasd_alias_update_add_device(device); + + dasd_eckd_get_uid(device, &uid); + + if (old_base != uid.base_unit_addr) { + if (strlen(uid.vduit) > 0) + snprintf(print_uid, sizeof(print_uid), + "%s.%s.%04x.%02x.%s", uid.vendor, uid.serial, + uid.ssid, uid.base_unit_addr, uid.vduit); + else + snprintf(print_uid, sizeof(print_uid), + "%s.%s.%04x.%02x", uid.vendor, uid.serial, + uid.ssid, uid.base_unit_addr); + + dev_info(&device->cdev->dev, + "An Alias device was reassigned to a new base device " + "with UID: %s\n", print_uid); + } + return 0; + +out_err: + return -1; +} + static struct ccw_driver dasd_eckd_driver = { .name = "dasd-eckd", .owner = THIS_MODULE, @@ -3389,6 +3477,8 @@ static struct dasd_discipline dasd_eckd_discipline = { .ioctl = dasd_eckd_ioctl, .freeze = dasd_eckd_pm_freeze, .restore = dasd_eckd_restore_device, + .reload = dasd_eckd_reload_device, + .get_uid = dasd_eckd_get_uid, }; static int __init diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 864d53c04201..dd6385a5af14 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -426,7 +426,6 @@ struct alias_pav_group { struct dasd_device *next; }; - struct dasd_eckd_private { struct dasd_eckd_characteristics rdc_data; u8 *conf_data; @@ -463,4 +462,5 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *); void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); void dasd_alias_lcu_setup_complete(struct dasd_device *); void dasd_alias_wait_for_lcu_setup(struct dasd_device *); +int dasd_alias_update_add_device(struct dasd_device *); #endif /* DASD_ECKD_H */ diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index a91d4a97d4f2..32fac186ba3f 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -81,6 +81,10 @@ struct dasd_block; #define DASD_SIM_MSG_TO_OP 0x03 #define DASD_SIM_LOG 0x0C +/* lock class for nested cdev lock */ +#define CDEV_NESTED_FIRST 1 +#define CDEV_NESTED_SECOND 2 + /* * SECTION: MACROs for klogd and s390 debug feature (dbf) */ @@ -229,6 +233,24 @@ struct dasd_ccw_req { typedef struct dasd_ccw_req *(*dasd_erp_fn_t) (struct dasd_ccw_req *); /* + * Unique identifier for dasd device. + */ +#define UA_NOT_CONFIGURED 0x00 +#define UA_BASE_DEVICE 0x01 +#define UA_BASE_PAV_ALIAS 0x02 +#define UA_HYPER_PAV_ALIAS 0x03 + +struct dasd_uid { + __u8 type; + char vendor[4]; + char serial[15]; + __u16 ssid; + __u8 real_unit_addr; + __u8 base_unit_addr; + char vduit[33]; +}; + +/* * the struct dasd_discipline is * sth like a table of virtual functions, if you think of dasd_eckd * inheriting dasd... @@ -312,28 +334,15 @@ struct dasd_discipline { /* suspend/resume functions */ int (*freeze) (struct dasd_device *); int (*restore) (struct dasd_device *); -}; -extern struct dasd_discipline *dasd_diag_discipline_pointer; - -/* - * Unique identifier for dasd device. - */ -#define UA_NOT_CONFIGURED 0x00 -#define UA_BASE_DEVICE 0x01 -#define UA_BASE_PAV_ALIAS 0x02 -#define UA_HYPER_PAV_ALIAS 0x03 + /* reload device after state change */ + int (*reload) (struct dasd_device *); -struct dasd_uid { - __u8 type; - char vendor[4]; - char serial[15]; - __u16 ssid; - __u8 real_unit_addr; - __u8 base_unit_addr; - char vduit[33]; + int (*get_uid) (struct dasd_device *, struct dasd_uid *); }; +extern struct dasd_discipline *dasd_diag_discipline_pointer; + /* * Notification numbers for extended error reporting notifications: * The DASD_EER_DISABLE notification is sent before a dasd_device (and it's @@ -386,6 +395,7 @@ struct dasd_device { struct tasklet_struct tasklet; struct work_struct kick_work; struct work_struct restore_device; + struct work_struct reload_device; struct timer_list timer; debug_info_t *debug_area; @@ -582,6 +592,7 @@ void dasd_enable_device(struct dasd_device *); void dasd_set_target_state(struct dasd_device *, int); void dasd_kick_device(struct dasd_device *); void dasd_restore_device(struct dasd_device *); +void dasd_reload_device(struct dasd_device *); void dasd_add_request_head(struct dasd_ccw_req *); void dasd_add_request_tail(struct dasd_ccw_req *); @@ -629,8 +640,6 @@ void dasd_devmap_exit(void); struct dasd_device *dasd_create_device(struct ccw_device *); void dasd_delete_device(struct dasd_device *); -int dasd_get_uid(struct ccw_device *, struct dasd_uid *); -int dasd_set_uid(struct ccw_device *, struct dasd_uid *); int dasd_get_feature(struct ccw_device *, int); int dasd_set_feature(struct ccw_device *, int, int); diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 4e34d3686c23..40834f18754c 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -148,13 +148,12 @@ config VMLOGRDR This driver depends on the IUCV support driver. config VMCP - tristate "Support for the z/VM CP interface (VM only)" + bool "Support for the z/VM CP interface" depends on S390 help Select this option if you want to be able to interact with the control program on z/VM - config MONREADER tristate "API for reading z/VM monitor service records" depends on IUCV diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 0eabcca3c92d..857dfcb7b359 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -484,6 +484,7 @@ fs3270_open(struct inode *inode, struct file *filp) raw3270_del_view(&fp->view); goto out; } + nonseekable_open(inode, filp); filp->private_data = fp; out: mutex_unlock(&fs3270_mutex); diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c index cb6bffe7141a..18d9a497863b 100644 --- a/drivers/s390/char/keyboard.c +++ b/drivers/s390/char/keyboard.c @@ -49,7 +49,7 @@ static unsigned char ret_diacr[NR_DEAD] = { struct kbd_data * kbd_alloc(void) { struct kbd_data *kbd; - int i, len; + int i; kbd = kzalloc(sizeof(struct kbd_data), GFP_KERNEL); if (!kbd) @@ -59,12 +59,11 @@ kbd_alloc(void) { goto out_kbd; for (i = 0; i < ARRAY_SIZE(key_maps); i++) { if (key_maps[i]) { - kbd->key_maps[i] = - kmalloc(sizeof(u_short)*NR_KEYS, GFP_KERNEL); + kbd->key_maps[i] = kmemdup(key_maps[i], + sizeof(u_short) * NR_KEYS, + GFP_KERNEL); if (!kbd->key_maps[i]) goto out_maps; - memcpy(kbd->key_maps[i], key_maps[i], - sizeof(u_short)*NR_KEYS); } } kbd->func_table = kzalloc(sizeof(func_table), GFP_KERNEL); @@ -72,23 +71,21 @@ kbd_alloc(void) { goto out_maps; for (i = 0; i < ARRAY_SIZE(func_table); i++) { if (func_table[i]) { - len = strlen(func_table[i]) + 1; - kbd->func_table[i] = kmalloc(len, GFP_KERNEL); + kbd->func_table[i] = kstrdup(func_table[i], + GFP_KERNEL); if (!kbd->func_table[i]) goto out_func; - memcpy(kbd->func_table[i], func_table[i], len); } } kbd->fn_handler = kzalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL); if (!kbd->fn_handler) goto out_func; - kbd->accent_table = - kmalloc(sizeof(struct kbdiacruc)*MAX_DIACR, GFP_KERNEL); + kbd->accent_table = kmemdup(accent_table, + sizeof(struct kbdiacruc) * MAX_DIACR, + GFP_KERNEL); if (!kbd->accent_table) goto out_fn_handler; - memcpy(kbd->accent_table, accent_table, - sizeof(struct kbdiacruc)*MAX_DIACR); kbd->accent_table_size = accent_table_size; return kbd; diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c index 62c2647f37f4..4a51e3f09689 100644 --- a/drivers/s390/char/sclp_cpi_sys.c +++ b/drivers/s390/char/sclp_cpi_sys.c @@ -102,7 +102,7 @@ static struct sclp_req *cpi_prepare_req(void) /* set system name */ set_data(evb->system_name, system_name); - /* set sytem level */ + /* set system level */ evb->system_level = system_level; /* set sysplex name */ diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index 5bb59d36a6d4..04e532eec032 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c @@ -1,24 +1,20 @@ /* - * Copyright IBM Corp. 2004,2007 + * Copyright IBM Corp. 2004,2010 * Interface implementation for communication with the z/VM control program - * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> * + * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> * * z/VMs CP offers the possibility to issue commands via the diagnose code 8 * this driver implements a character device that issues these commands and * returns the answer of CP. - + * * The idea of this driver is based on cpint from Neale Ferguson and #CP in CMS */ -#define KMSG_COMPONENT "vmcp" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt - #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/miscdevice.h> -#include <linux/module.h> #include <linux/slab.h> #include <asm/compat.h> #include <asm/cpcmd.h> @@ -26,10 +22,6 @@ #include <asm/uaccess.h> #include "vmcp.h" -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Borntraeger <borntraeger@de.ibm.com>"); -MODULE_DESCRIPTION("z/VM CP interface"); - static debug_info_t *vmcp_debug; static int vmcp_open(struct inode *inode, struct file *file) @@ -197,11 +189,8 @@ static int __init vmcp_init(void) { int ret; - if (!MACHINE_IS_VM) { - pr_warning("The z/VM CP interface device driver cannot be " - "loaded without z/VM\n"); - return -ENODEV; - } + if (!MACHINE_IS_VM) + return 0; vmcp_debug = debug_register("vmcp", 1, 1, 240); if (!vmcp_debug) @@ -214,19 +203,8 @@ static int __init vmcp_init(void) } ret = misc_register(&vmcp_dev); - if (ret) { + if (ret) debug_unregister(vmcp_debug); - return ret; - } - - return 0; -} - -static void __exit vmcp_exit(void) -{ - misc_deregister(&vmcp_dev); - debug_unregister(vmcp_debug); + return ret; } - -module_init(vmcp_init); -module_exit(vmcp_exit); +device_initcall(vmcp_init); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 7217966f7d31..f5ea3384a4b9 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -445,7 +445,7 @@ static int zcore_memmap_open(struct inode *inode, struct file *filp) } kfree(chunk_array); filp->private_data = buf; - return 0; + return nonseekable_open(inode, filp); } static int zcore_memmap_release(struct inode *inode, struct file *filp) @@ -473,7 +473,7 @@ static ssize_t zcore_reipl_write(struct file *filp, const char __user *buf, static int zcore_reipl_open(struct inode *inode, struct file *filp) { - return 0; + return nonseekable_open(inode, filp); } static int zcore_reipl_release(struct inode *inode, struct file *filp) diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index 3b6f4adc5094..a83877c664a6 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -803,6 +803,7 @@ static long chsc_ioctl(struct file *filp, unsigned int cmd, static const struct file_operations chsc_fops = { .owner = THIS_MODULE, + .open = nonseekable_open, .unlocked_ioctl = chsc_ioctl, .compat_ioctl = chsc_ioctl, }; diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 5feea1a371e1..f4e6cf3aceb8 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -616,7 +616,8 @@ void __irq_entry do_IRQ(struct pt_regs *regs) struct pt_regs *old_regs; old_regs = set_irq_regs(regs); - s390_idle_check(); + s390_idle_check(regs, S390_lowcore.int_clock, + S390_lowcore.async_enter_timer); irq_enter(); __get_cpu_var(s390_idle).nohz_delay = 1; if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 511649115bd7..ac94ac751459 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -648,6 +648,8 @@ static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) static void __init css_generate_pgid(struct channel_subsystem *css, u32 tod_high) { + struct cpuid cpu_id; + if (css_general_characteristics.mcss) { css->global_pgid.pgid_high.ext_cssid.version = 0x80; css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid; @@ -658,8 +660,9 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high) css->global_pgid.pgid_high.cpu_addr = 0; #endif } - css->global_pgid.cpu_id = S390_lowcore.cpu_id.ident; - css->global_pgid.cpu_model = S390_lowcore.cpu_id.machine; + get_cpu_id(&cpu_id); + css->global_pgid.cpu_id = cpu_id.ident; + css->global_pgid.cpu_model = cpu_id.machine; css->global_pgid.tod_high = tod_high; } @@ -1062,6 +1065,7 @@ static ssize_t cio_settle_write(struct file *file, const char __user *buf, } static const struct file_operations cio_settle_proc_fops = { + .open = nonseekable_open, .write = cio_settle_write, }; diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 48aa0647432b..f0037eefd44e 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -13,8 +13,8 @@ #include <asm/debug.h> #include "chsc.h" -#define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */ -#define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */ +#define QDIO_BUSY_BIT_PATIENCE (100 << 12) /* 100 microseconds */ +#define QDIO_INPUT_THRESHOLD (500 << 12) /* 500 microseconds */ /* * if an asynchronous HiperSockets queue runs full, the 10 seconds timer wait @@ -296,10 +296,8 @@ struct qdio_q { struct qdio_irq *irq_ptr; struct sl *sl; /* - * Warning: Leave this member at the end so it won't be cleared in - * qdio_fill_qs. A page is allocated under this pointer and used for - * slib and sl. slib is 2048 bytes big and sl points to offset - * PAGE_SIZE / 2. + * A page is allocated under this pointer and used for slib and sl. + * slib is 2048 bytes big and sl points to offset PAGE_SIZE / 2. */ struct slib *slib; } __attribute__ ((aligned(256))); @@ -372,11 +370,6 @@ static inline int multicast_outbound(struct qdio_q *q) (q->nr == q->irq_ptr->nr_output_qs - 1); } -static inline unsigned long long get_usecs(void) -{ - return monotonic_clock() >> 12; -} - #define pci_out_supported(q) \ (q->irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) #define is_qebsm(q) (q->irq_ptr->sch_token != 0) diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 88be7b9ea6e1..00520f9a7a8e 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -336,10 +336,10 @@ again: WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2); if (!start_time) { - start_time = get_usecs(); + start_time = get_clock(); goto again; } - if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE) + if ((get_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE) goto again; } return cc; @@ -536,7 +536,7 @@ static int qdio_inbound_q_moved(struct qdio_q *q) if ((bufnr != q->last_move) || q->qdio_error) { q->last_move = bufnr; if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR) - q->u.in.timestamp = get_usecs(); + q->u.in.timestamp = get_clock(); return 1; } else return 0; @@ -567,7 +567,7 @@ static inline int qdio_inbound_q_done(struct qdio_q *q) * At this point we know, that inbound first_to_check * has (probably) not moved (see qdio_inbound_processing). */ - if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { + if (get_clock() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%02x", q->first_to_check); return 1; @@ -606,7 +606,7 @@ static void qdio_kick_handler(struct qdio_q *q) static void __qdio_inbound_processing(struct qdio_q *q) { qperf_inc(q, tasklet_inbound); -again: + if (!qdio_inbound_q_moved(q)) return; @@ -615,7 +615,10 @@ again: if (!qdio_inbound_q_done(q)) { /* means poll time is not yet over */ qperf_inc(q, tasklet_inbound_resched); - goto again; + if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) { + tasklet_schedule(&q->tasklet); + return; + } } qdio_stop_polling(q); @@ -625,7 +628,8 @@ again: */ if (!qdio_inbound_q_done(q)) { qperf_inc(q, tasklet_inbound_resched2); - goto again; + if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) + tasklet_schedule(&q->tasklet); } } @@ -955,6 +959,9 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } + if (irq_ptr->perf_stat_enabled) + irq_ptr->perf_stat.qdio_int++; + if (IS_ERR(irb)) { switch (PTR_ERR(irb)) { case -EIO: @@ -1016,30 +1023,6 @@ int qdio_get_ssqd_desc(struct ccw_device *cdev, } EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc); -/** - * qdio_cleanup - shutdown queues and free data structures - * @cdev: associated ccw device - * @how: use halt or clear to shutdown - * - * This function calls qdio_shutdown() for @cdev with method @how. - * and qdio_free(). The qdio_free() return value is ignored since - * !irq_ptr is already checked. - */ -int qdio_cleanup(struct ccw_device *cdev, int how) -{ - struct qdio_irq *irq_ptr = cdev->private->qdio_data; - int rc; - - if (!irq_ptr) - return -ENODEV; - - rc = qdio_shutdown(cdev, how); - - qdio_free(cdev); - return rc; -} -EXPORT_SYMBOL_GPL(qdio_cleanup); - static void qdio_shutdown_queues(struct ccw_device *cdev) { struct qdio_irq *irq_ptr = cdev->private->qdio_data; @@ -1157,28 +1140,6 @@ int qdio_free(struct ccw_device *cdev) EXPORT_SYMBOL_GPL(qdio_free); /** - * qdio_initialize - allocate and establish queues for a qdio subchannel - * @init_data: initialization data - * - * This function first allocates queues via qdio_allocate() and on success - * establishes them via qdio_establish(). - */ -int qdio_initialize(struct qdio_initialize *init_data) -{ - int rc; - - rc = qdio_allocate(init_data); - if (rc) - return rc; - - rc = qdio_establish(init_data); - if (rc) - qdio_free(init_data->cdev); - return rc; -} -EXPORT_SYMBOL_GPL(qdio_initialize); - -/** * qdio_allocate - allocate qdio queues and associated data * @init_data: initialization data */ diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 7f4a75465140..6326b67c45d2 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -106,10 +106,12 @@ int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs, int nr_output_qs static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr, qdio_handler_t *handler, int i) { - /* must be cleared by every qdio_establish */ - memset(q, 0, ((char *)&q->slib) - ((char *)q)); - memset(q->slib, 0, PAGE_SIZE); + struct slib *slib = q->slib; + /* queue must be cleared for qdio_establish */ + memset(q, 0, sizeof(*q)); + memset(slib, 0, PAGE_SIZE); + q->slib = slib; q->irq_ptr = irq_ptr; q->mask = 1 << (31 - i); q->nr = i; diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index ce5f8910ff83..8daf1b99f153 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -95,7 +95,7 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) for_each_input_queue(irq_ptr, q, i) list_add_rcu(&q->entry, &tiq_list); mutex_unlock(&tiq_list_lock); - xchg(irq_ptr->dsci, 1); + xchg(irq_ptr->dsci, 1 << 7); } void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) @@ -173,7 +173,7 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data) /* prevent racing */ if (*tiqdio_alsi) - xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1); + xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7); } } diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 304caf549973..41e0aaefafd5 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -302,7 +302,7 @@ static ssize_t zcrypt_write(struct file *filp, const char __user *buf, static int zcrypt_open(struct inode *inode, struct file *filp) { atomic_inc(&zcrypt_open_count); - return 0; + return nonseekable_open(inode, filp); } /** diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 3ba738b2e271..28f71349fdec 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1292,13 +1292,14 @@ int qeth_qdio_clear_card(struct qeth_card *card, int use_halt) QETH_QDIO_CLEANING)) { case QETH_QDIO_ESTABLISHED: if (card->info.type == QETH_CARD_TYPE_IQD) - rc = qdio_cleanup(CARD_DDEV(card), + rc = qdio_shutdown(CARD_DDEV(card), QDIO_FLAG_CLEANUP_USING_HALT); else - rc = qdio_cleanup(CARD_DDEV(card), + rc = qdio_shutdown(CARD_DDEV(card), QDIO_FLAG_CLEANUP_USING_CLEAR); if (rc) QETH_DBF_TEXT_(TRACE, 3, "1err%d", rc); + qdio_free(CARD_DDEV(card)); atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); break; case QETH_QDIO_CLEANING: @@ -3810,10 +3811,18 @@ static int qeth_qdio_establish(struct qeth_card *card) if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { - rc = qdio_initialize(&init_data); - if (rc) + rc = qdio_allocate(&init_data); + if (rc) { + atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); + goto out; + } + rc = qdio_establish(&init_data); + if (rc) { atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); + qdio_free(CARD_DDEV(card)); + } } +out: kfree(out_sbal_ptrs); kfree(in_sbal_ptrs); kfree(qib_param_field); diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c index 25d9e0ae9c57..1a2db0a35737 100644 --- a/drivers/s390/scsi/zfcp_cfdc.c +++ b/drivers/s390/scsi/zfcp_cfdc.c @@ -254,6 +254,7 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command, } static const struct file_operations zfcp_cfdc_fops = { + .open = nonseekable_open, .unlocked_ioctl = zfcp_cfdc_dev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = zfcp_cfdc_dev_ioctl diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index 19f255b97c86..d3b62eb0fba7 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -105,9 +105,9 @@ static ssize_t flash_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { - unsigned long p = file->f_pos; + loff_t p = *ppos; int i; - + if (count > flash.read_size - p) count = flash.read_size - p; @@ -118,7 +118,7 @@ flash_read(struct file * file, char __user * buf, buf++; } - file->f_pos += count; + *ppos += count; return count; } diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_cee.h b/drivers/scsi/bfa/include/defs/bfa_defs_cee.h index b0ac9ac15c5d..6eaf519eccdc 100644 --- a/drivers/scsi/bfa/include/defs/bfa_defs_cee.h +++ b/drivers/scsi/bfa/include/defs/bfa_defs_cee.h @@ -50,7 +50,7 @@ struct bfa_cee_lldp_str_s { }; -/* LLDP paramters */ +/* LLDP parameters */ struct bfa_cee_lldp_cfg_s { struct bfa_cee_lldp_str_s chassis_id; struct bfa_cee_lldp_str_s port_id; diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_status.h b/drivers/scsi/bfa/include/defs/bfa_defs_status.h index 4374494bd566..ec78b4cb121a 100644 --- a/drivers/scsi/bfa/include/defs/bfa_defs_status.h +++ b/drivers/scsi/bfa/include/defs/bfa_defs_status.h @@ -223,9 +223,9 @@ enum bfa_status { BFA_STATUS_IM_PVID_NON_ZERO = 140, /* Port VLAN ID (PVID) is Set to * Non-Zero Value */ BFA_STATUS_IM_INETCFG_LOCK_FAILED = 141, /* Acquiring Network - * Subsytem Lock Failed.Please + * Subsystem Lock Failed.Please * try after some time */ - BFA_STATUS_IM_GET_INETCFG_FAILED = 142, /* Acquiring Network Subsytem + BFA_STATUS_IM_GET_INETCFG_FAILED = 142, /* Acquiring Network Subsystem * handle Failed. Please try * after some time */ BFA_STATUS_IM_NOT_BOUND = 143, /* IM driver is not active */ diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 520461b9bc09..b90c118119d7 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4295,7 +4295,7 @@ static void ipr_slave_destroy(struct scsi_device *sdev) res = (struct ipr_resource_entry *) sdev->hostdata; if (res) { if (res->sata_port) - ata_port_disable(res->sata_port->ap); + res->sata_port->ap->link.device[0].class = ATA_DEV_NONE; sdev->hostdata = NULL; res->sdev = NULL; res->sata_port = NULL; @@ -5751,13 +5751,13 @@ static void ipr_ata_phy_reset(struct ata_port *ap) rc = ipr_device_reset(ioa_cfg, res); if (rc) { - ata_port_disable(ap); + ap->link.device[0].class = ATA_DEV_NONE; goto out_unlock; } ap->link.device[0].class = res->ata_class; if (ap->link.device[0].class == ATA_DEV_UNKNOWN) - ata_port_disable(ap); + ap->link.device[0].class = ATA_DEV_NONE; out_unlock: spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 822835055cef..b71b6d41baa1 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -818,7 +818,7 @@ void sas_slave_destroy(struct scsi_device *scsi_dev) struct domain_device *dev = sdev_to_domain_dev(scsi_dev); if (dev_is_sata(dev)) - ata_port_disable(dev->sata_dev.ap); + dev->sata_dev.ap->link.device[0].class = ATA_DEV_NONE; } int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth, diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c index 528733b4a392..9d70aef99227 100644 --- a/drivers/scsi/pcmcia/aha152x_stub.c +++ b/drivers/scsi/pcmcia/aha152x_stub.c @@ -80,7 +80,6 @@ MODULE_LICENSE("Dual MPL/GPL"); typedef struct scsi_info_t { struct pcmcia_device *p_dev; - dev_node_t node; struct Scsi_Host *host; } scsi_info_t; @@ -105,7 +104,6 @@ static int aha152x_probe(struct pcmcia_device *link) link->io.NumPorts1 = 0x20; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 10; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; @@ -160,8 +158,7 @@ static int aha152x_config_cs(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) + if (!link->irq) goto failed; ret = pcmcia_request_configuration(link, &link->conf); @@ -172,7 +169,7 @@ static int aha152x_config_cs(struct pcmcia_device *link) memset(&s, 0, sizeof(s)); s.conf = "PCMCIA setup"; s.io_port = link->io.BasePort1; - s.irq = link->irq.AssignedIRQ; + s.irq = link->irq; s.scsiid = host_id; s.reconnect = reconnect; s.parity = parity; @@ -187,8 +184,6 @@ static int aha152x_config_cs(struct pcmcia_device *link) goto failed; } - sprintf(info->node.dev_name, "scsi%d", host->host_no); - link->dev_node = &info->node; info->host = host; return 0; diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c index 914040684079..21b141151dfc 100644 --- a/drivers/scsi/pcmcia/fdomain_stub.c +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -63,7 +63,6 @@ MODULE_LICENSE("Dual MPL/GPL"); typedef struct scsi_info_t { struct pcmcia_device *p_dev; - dev_node_t node; struct Scsi_Host *host; } scsi_info_t; @@ -88,7 +87,6 @@ static int fdomain_probe(struct pcmcia_device *link) link->io.NumPorts1 = 0x10; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 10; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; @@ -133,8 +131,7 @@ static int fdomain_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) + if (!link->irq) goto failed; ret = pcmcia_request_configuration(link, &link->conf); if (ret) @@ -144,7 +141,7 @@ static int fdomain_config(struct pcmcia_device *link) release_region(link->io.BasePort1, link->io.NumPorts1); /* Set configuration options for the fdomain driver */ - sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ); + sprintf(str, "%d,%d", link->io.BasePort1, link->irq); fdomain_setup(str); host = __fdomain_16x0_detect(&fdomain_driver_template); @@ -157,8 +154,6 @@ static int fdomain_config(struct pcmcia_device *link) goto failed; scsi_scan_host(host); - sprintf(info->node.dev_name, "scsi%d", host->host_no); - link->dev_node = &info->node; info->host = host; return 0; diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index 021246454872..0f0e112c3f8e 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -1563,13 +1563,6 @@ static int nsp_cs_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 10; /* not used */ - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - - /* Interrupt handler */ - link->irq.Handler = &nspintr; - link->irq.Attributes |= IRQF_SHARED; - /* General socket configuration */ link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -1646,8 +1639,7 @@ static int nsp_cs_config_check(struct pcmcia_device *p_dev, } /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -1720,10 +1712,8 @@ static int nsp_cs_config(struct pcmcia_device *link) if (ret) goto cs_failed; - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - if (pcmcia_request_irq(link, &link->irq)) - goto cs_failed; - } + if (pcmcia_request_irq(link, nspintr)) + goto cs_failed; ret = pcmcia_request_configuration(link, &link->conf); if (ret) @@ -1741,7 +1731,7 @@ static int nsp_cs_config(struct pcmcia_device *link) /* Set port and IRQ */ data->BaseAddress = link->io.BasePort1; data->NumAddress = link->io.NumPorts1; - data->IrqNumber = link->irq.AssignedIRQ; + data->IrqNumber = link->irq; nsp_dbg(NSP_DEBUG_INIT, "I/O[0x%x+0x%x] IRQ %d", data->BaseAddress, data->NumAddress, data->IrqNumber); @@ -1764,8 +1754,6 @@ static int nsp_cs_config(struct pcmcia_device *link) scsi_scan_host(host); - snprintf(info->node.dev_name, sizeof(info->node.dev_name), "scsi%d", host->host_no); - link->dev_node = &info->node; info->host = host; /* Finally, report what we've done */ @@ -1775,7 +1763,7 @@ static int nsp_cs_config(struct pcmcia_device *link) printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10); } if (link->conf.Attributes & CONF_ENABLE_IRQ) { - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); } if (link->io.NumPorts1) { printk(", io 0x%04x-0x%04x", link->io.BasePort1, @@ -1823,7 +1811,6 @@ static void nsp_cs_release(struct pcmcia_device *link) if (info->host != NULL) { scsi_remove_host(info->host); } - link->dev_node = NULL; if (link->win) { if (data != NULL) { diff --git a/drivers/scsi/pcmcia/nsp_cs.h b/drivers/scsi/pcmcia/nsp_cs.h index 8c61a4fe1db9..d68c9f267c5e 100644 --- a/drivers/scsi/pcmcia/nsp_cs.h +++ b/drivers/scsi/pcmcia/nsp_cs.h @@ -224,7 +224,6 @@ typedef struct scsi_info_t { struct pcmcia_device *p_dev; struct Scsi_Host *host; - dev_node_t node; int stop; } scsi_info_t; diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c index f85f094870b4..f0fc6baed9fc 100644 --- a/drivers/scsi/pcmcia/qlogic_stub.c +++ b/drivers/scsi/pcmcia/qlogic_stub.c @@ -82,7 +82,6 @@ static struct scsi_host_template qlogicfas_driver_template = { typedef struct scsi_info_t { struct pcmcia_device *p_dev; - dev_node_t node; struct Scsi_Host *host; unsigned short manf_id; } scsi_info_t; @@ -161,7 +160,6 @@ static int qlogic_probe(struct pcmcia_device *link) link->io.NumPorts1 = 16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 10; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; @@ -209,8 +207,7 @@ static int qlogic_config(struct pcmcia_device * link) if (ret) goto failed; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) + if (!link->irq) goto failed; ret = pcmcia_request_configuration(link, &link->conf); @@ -227,18 +224,16 @@ static int qlogic_config(struct pcmcia_device * link) /* The KXL-810AN has a bigger IO port window */ if (link->io.NumPorts1 == 32) host = qlogic_detect(&qlogicfas_driver_template, link, - link->io.BasePort1 + 16, link->irq.AssignedIRQ); + link->io.BasePort1 + 16, link->irq); else host = qlogic_detect(&qlogicfas_driver_template, link, - link->io.BasePort1, link->irq.AssignedIRQ); + link->io.BasePort1, link->irq); if (!host) { printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name); goto failed; } - sprintf(info->node.dev_name, "scsi%d", host->host_no); - link->dev_node = &info->node; info->host = host; return 0; @@ -258,7 +253,7 @@ static void qlogic_release(struct pcmcia_device *link) scsi_remove_host(info->host); - free_irq(link->irq.AssignedIRQ, info->host); + free_irq(link->irq, info->host); pcmcia_disable_device(link); scsi_host_put(info->host); diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c index e7564d8f0cbf..a51164171179 100644 --- a/drivers/scsi/pcmcia/sym53c500_cs.c +++ b/drivers/scsi/pcmcia/sym53c500_cs.c @@ -191,7 +191,6 @@ struct scsi_info_t { struct pcmcia_device *p_dev; - dev_node_t node; struct Scsi_Host *host; unsigned short manf_id; }; @@ -719,8 +718,7 @@ SYM53C500_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_irq(link, &link->irq); - if (ret) + if (!link->irq) goto failed; ret = pcmcia_request_configuration(link, &link->conf); @@ -752,7 +750,7 @@ SYM53C500_config(struct pcmcia_device *link) * 0x320, 0x330, 0x340, 0x350 */ port_base = link->io.BasePort1; - irq_level = link->irq.AssignedIRQ; + irq_level = link->irq; DEB(printk("SYM53C500: port_base=0x%x, irq=%d, fast_pio=%d\n", port_base, irq_level, USE_FAST_PIO);) @@ -793,8 +791,6 @@ SYM53C500_config(struct pcmcia_device *link) */ data->fast_pio = USE_FAST_PIO; - sprintf(info->node.dev_name, "scsi%d", host->host_no); - link->dev_node = &info->node; info->host = host; if (scsi_add_host(host, NULL)) @@ -866,7 +862,6 @@ SYM53C500_probe(struct pcmcia_device *link) link->io.NumPorts1 = 16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 10; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; diff --git a/drivers/scsi/zorro7xx.c b/drivers/scsi/zorro7xx.c index 105449c15fa9..e17764d71476 100644 --- a/drivers/scsi/zorro7xx.c +++ b/drivers/scsi/zorro7xx.c @@ -69,6 +69,7 @@ static struct zorro_device_id zorro7xx_zorro_tbl[] __devinitdata = { }, { 0 } }; +MODULE_DEVICE_TABLE(zorro, zorro7xx_zorro_tbl); static int __devinit zorro7xx_init_one(struct zorro_dev *z, const struct zorro_device_id *ent) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f55c49475a8c..302836a80693 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -518,12 +518,13 @@ config SERIAL_S3C2412 Serial port support for the Samsung S3C2412 and S3C2413 SoC config SERIAL_S3C2440 - tristate "Samsung S3C2440/S3C2442 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442) + tristate "Samsung S3C2440/S3C2442/S3C2416 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442 || CPU_S3C2416) default y if CPU_S3C2440 default y if CPU_S3C2442 + select SERIAL_SAMSUNG_UARTS_4 if CPU_S3C2416 help - Serial port support for the Samsung S3C2440 and S3C2442 SoC + Serial port support for the Samsung S3C2440, S3C2416 and S3C2442 SoC config SERIAL_S3C24A0 tristate "Samsung S3C24A0 Serial port support" @@ -533,21 +534,13 @@ config SERIAL_S3C24A0 Serial port support for the Samsung S3C24A0 SoC config SERIAL_S3C6400 - tristate "Samsung S3C6400/S3C6410/S5P6440 Seria port support" - depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440) + tristate "Samsung S3C6400/S3C6410/S5P6440/S5PC100 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440 || CPU_S5PC100) select SERIAL_SAMSUNG_UARTS_4 default y help - Serial port support for the Samsung S3C6400, S3C6410 and S5P6440 - SoCs - -config SERIAL_S5PC100 - tristate "Samsung S5PC100 Serial port support" - depends on SERIAL_SAMSUNG && CPU_S5PC100 - select SERIAL_SAMSUNG_UARTS_4 - default y - help - Serial port support for the Samsung S5PC100 SoCs + Serial port support for the Samsung S3C6400, S3C6410, S5P6440 + and S5PC100 SoCs config SERIAL_S5PV210 tristate "Samsung S5PV210 Serial port support" diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 6aa4723b74ee..328f107346c4 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o -obj-$(CONFIG_SERIAL_S5PC100) += s3c6400.o obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 2c9bf9b68327..eed3c2d8dd1c 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -38,6 +38,7 @@ #include <linux/dma-mapping.h> #include <linux/atmel_pdc.h> #include <linux/atmel_serial.h> +#include <linux/uaccess.h> #include <asm/io.h> @@ -59,6 +60,9 @@ #include <linux/serial_core.h> +static void atmel_start_rx(struct uart_port *port); +static void atmel_stop_rx(struct uart_port *port); + #ifdef CONFIG_SERIAL_ATMEL_TTYAT /* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we @@ -93,6 +97,7 @@ #define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) +#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) /* PDC registers */ #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) @@ -147,6 +152,9 @@ struct atmel_uart_port { unsigned int irq_status_prev; struct circ_buf rx_ring; + + struct serial_rs485 rs485; /* rs485 settings */ + unsigned int tx_done_mask; }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; @@ -187,6 +195,46 @@ static bool atmel_use_dma_tx(struct uart_port *port) } #endif +/* Enable or disable the rs485 support */ +void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int mode; + + spin_lock(&port->lock); + + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + mode = UART_GET_MR(port); + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + atmel_port->rs485 = *rs485conf; + + if (rs485conf->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + UART_PUT_TTGR(port, rs485conf->delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + if (atmel_use_dma_tx(port)) + atmel_port->tx_done_mask = ATMEL_US_ENDTX | + ATMEL_US_TXBUFE; + else + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } + UART_PUT_MR(port, mode); + + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + + spin_unlock(&port->lock); + +} + /* * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. */ @@ -202,6 +250,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) { unsigned int control = 0; unsigned int mode; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); #ifdef CONFIG_ARCH_AT91RM9200 if (cpu_is_at91rm9200()) { @@ -236,6 +285,17 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) mode |= ATMEL_US_CHMODE_LOC_LOOP; else mode |= ATMEL_US_CHMODE_NORMAL; + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } UART_PUT_MR(port, mode); } @@ -268,12 +328,17 @@ static u_int atmel_get_mctrl(struct uart_port *port) */ static void atmel_stop_tx(struct uart_port *port) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_use_dma_tx(port)) { /* disable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); - } else - UART_PUT_IDR(port, ATMEL_US_TXRDY); + } + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_start_rx(port); } /* @@ -281,17 +346,39 @@ static void atmel_stop_tx(struct uart_port *port) */ static void atmel_start_tx(struct uart_port *port) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_use_dma_tx(port)) { if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) /* The transmitter is already running. Yes, we really need this.*/ return; - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_stop_rx(port); + /* re-enable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - } else - UART_PUT_IER(port, ATMEL_US_TXRDY); + } + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); +} + +/* + * start receiving - port is in process of being opened. + */ +static void atmel_start_rx(struct uart_port *port) +{ + UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */ + + if (atmel_use_dma_rx(port)) { + /* enable PDC controller */ + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else { + UART_PUT_IER(port, ATMEL_US_RXRDY); + } } /* @@ -302,9 +389,11 @@ static void atmel_stop_rx(struct uart_port *port) if (atmel_use_dma_rx(port)) { /* disable PDC receive */ UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); - } else + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + } else { UART_PUT_IDR(port, ATMEL_US_RXRDY); + } } /* @@ -428,8 +517,9 @@ static void atmel_rx_chars(struct uart_port *port) static void atmel_tx_chars(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) { + if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) { UART_PUT_CHAR(port, port->x_char); port->icount.tx++; port->x_char = 0; @@ -437,7 +527,7 @@ static void atmel_tx_chars(struct uart_port *port) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; - while (UART_GET_CSR(port) & ATMEL_US_TXRDY) { + while (UART_GET_CSR(port) & atmel_port->tx_done_mask) { UART_PUT_CHAR(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; @@ -449,7 +539,8 @@ static void atmel_tx_chars(struct uart_port *port) uart_write_wakeup(port); if (!uart_circ_empty(xmit)) - UART_PUT_IER(port, ATMEL_US_TXRDY); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); } /* @@ -501,18 +592,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { - /* PDC transmit */ - if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) { - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); - tasklet_schedule(&atmel_port->tasklet); - } - } else { - /* Interrupt transmit */ - if (pending & ATMEL_US_TXRDY) { - UART_PUT_IDR(port, ATMEL_US_TXRDY); - tasklet_schedule(&atmel_port->tasklet); - } + if (pending & atmel_port->tx_done_mask) { + /* Either PDC or interrupt transmission */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + tasklet_schedule(&atmel_port->tasklet); } } @@ -590,9 +673,15 @@ static void atmel_tx_dma(struct uart_port *port) UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); UART_PUT_TCR(port, count); - /* re-enable PDC transmit and interrupts */ + /* re-enable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -1017,6 +1106,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, { unsigned long flags; unsigned int mode, imr, quot, baud; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); /* Get current mode register */ mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL @@ -1115,6 +1205,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, /* disable receiver and transmitter */ UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } + /* set the parity, stop bits and data size */ UART_PUT_MR(port, mode); @@ -1231,6 +1332,35 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) } #endif +static int +atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + sizeof(rs485conf))) + return -EFAULT; + + atmel_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((struct serial_rs485 *) arg, + &(to_atmel_uart_port(port)->rs485), + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + + + static struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, @@ -1250,6 +1380,7 @@ static struct uart_ops atmel_pops = { .config_port = atmel_config_port, .verify_port = atmel_verify_port, .pm = atmel_serial_pm, + .ioctl = atmel_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = atmel_poll_get_char, .poll_put_char = atmel_poll_put_char, @@ -1265,13 +1396,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct uart_port *port = &atmel_port->uart; struct atmel_uart_data *data = pdev->dev.platform_data; - port->iotype = UPIO_MEM; - port->flags = UPF_BOOT_AUTOCONF; - port->ops = &atmel_pops; - port->fifosize = 1; - port->line = pdev->id; - port->dev = &pdev->dev; - + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &atmel_pops; + port->fifosize = 1; + port->line = pdev->id; + port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; @@ -1299,8 +1429,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, atmel_port->use_dma_rx = data->use_dma_rx; atmel_port->use_dma_tx = data->use_dma_tx; - if (atmel_use_dma_tx(port)) + atmel_port->rs485 = data->rs485; + /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + else if (atmel_use_dma_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; + atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; + } else { + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } } /* @@ -1334,6 +1472,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch) static void atmel_console_write(struct console *co, const char *s, u_int count) { struct uart_port *port = &atmel_ports[co->index].uart; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int status, imr; unsigned int pdc_tx; @@ -1341,7 +1480,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count) * First, save IMR and then disable interrupts */ imr = UART_GET_IMR(port); - UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY); + UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask); /* Store PDC transmit status and disable it */ pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN; diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 8cfa5b12ea7a..dadd686c9801 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -89,7 +89,6 @@ struct serial_info { int manfid; int prodid; int c950ctrl; - dev_node_t node[4]; int line[4]; const struct serial_quirk *quirk; }; @@ -289,8 +288,6 @@ static void serial_remove(struct pcmcia_device *link) for (i = 0; i < info->ndev; i++) serial8250_unregister_port(info->line[i]); - info->p_dev->dev_node = NULL; - if (!info->slave) pcmcia_disable_device(link); } @@ -343,7 +340,6 @@ static int serial_probe(struct pcmcia_device *link) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; if (do_sound) { link->conf.Attributes |= CONF_ENABLE_SPKR; @@ -411,11 +407,6 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, } info->line[info->ndev] = line; - sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); - info->node[info->ndev].major = TTY_MAJOR; - info->node[info->ndev].minor = 0x40 + line; - if (info->ndev > 0) - info->node[info->ndev - 1].next = &info->node[info->ndev]; info->ndev++; return 0; @@ -486,7 +477,7 @@ static int simple_config(struct pcmcia_device *link) } if (info->slave) { return setup_serial(link, info, port, - link->irq.AssignedIRQ); + link->irq); } } @@ -507,10 +498,6 @@ static int simple_config(struct pcmcia_device *link) return -1; found_port: - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) - link->irq.AssignedIRQ = 0; - if (info->multi && (info->manfid == MANFID_3COM)) link->conf.ConfigIndex &= ~(0x08); @@ -523,7 +510,7 @@ found_port: i = pcmcia_request_configuration(link, &link->conf); if (i != 0) return -1; - return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); + return setup_serial(link, info, link->io.BasePort1, link->irq); } static int multi_config_check(struct pcmcia_device *p_dev, @@ -586,13 +573,9 @@ static int multi_config(struct pcmcia_device *link) } } - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { - /* FIXME: comment does not fit, error handling does not fit */ - printk(KERN_NOTICE - "serial_cs: no usable port range found, giving up\n"); - link->irq.AssignedIRQ = 0; - } + if (!link->irq) + dev_warn(&link->dev, + "serial_cs: no usable IRQ found, continuing...\n"); /* * Apply any configuration quirks. @@ -615,11 +598,11 @@ static int multi_config(struct pcmcia_device *link) if (link->conf.ConfigIndex == 1 || link->conf.ConfigIndex == 3) { err = setup_serial(link, info, base2, - link->irq.AssignedIRQ); + link->irq); base2 = link->io.BasePort1; } else { err = setup_serial(link, info, link->io.BasePort1, - link->irq.AssignedIRQ); + link->irq); } info->c950ctrl = base2; @@ -633,10 +616,10 @@ static int multi_config(struct pcmcia_device *link) return 0; } - setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); + setup_serial(link, info, link->io.BasePort1, link->irq); for (i = 0; i < info->multi - 1; i++) setup_serial(link, info, base2 + (8 * i), - link->irq.AssignedIRQ); + link->irq); return 0; } @@ -720,7 +703,6 @@ static int serial_config(struct pcmcia_device * link) if (info->quirk->post(link)) goto failed; - link->dev_node = &info->node[0]; return 0; failed: diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 8eb094c1f61b..8d993c4cceac 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -83,16 +83,16 @@ struct sci_port { /* Interface clock */ struct clk *iclk; - /* Data clock */ - struct clk *dclk; + /* Function clock */ + struct clk *fclk; struct list_head node; struct dma_chan *chan_tx; struct dma_chan *chan_rx; #ifdef CONFIG_SERIAL_SH_SCI_DMA struct device *dma_dev; - enum sh_dmae_slave_chan_id slave_tx; - enum sh_dmae_slave_chan_id slave_rx; + unsigned int slave_tx; + unsigned int slave_rx; struct dma_async_tx_descriptor *desc_tx; struct dma_async_tx_descriptor *desc_rx[2]; dma_cookie_t cookie_tx; @@ -107,6 +107,7 @@ struct sci_port { struct work_struct work_tx; struct work_struct work_rx; struct timer_list rx_timer; + unsigned int rx_timeout; #endif }; @@ -674,22 +675,22 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) struct sci_port *s = to_sci_port(port); if (s->chan_rx) { - unsigned long tout; u16 scr = sci_in(port, SCSCR); u16 ssr = sci_in(port, SCxSR); /* Disable future Rx interrupts */ - sci_out(port, SCSCR, scr & ~SCI_CTRL_FLAGS_RIE); + if (port->type == PORT_SCIFA) { + disable_irq_nosync(irq); + scr |= 0x4000; + } else { + scr &= ~SCI_CTRL_FLAGS_RIE; + } + sci_out(port, SCSCR, scr); /* Clear current interrupt */ sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); - /* Calculate delay for 1.5 DMA buffers */ - tout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / - port->fifosize / 2; - dev_dbg(port->dev, "Rx IRQ: setup timeout in %lu ms\n", - tout * 1000 / HZ); - if (tout < 2) - tout = 2; - mod_timer(&s->rx_timer, jiffies + tout); + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + jiffies, s->rx_timeout); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); return IRQ_HANDLED; } @@ -799,7 +800,7 @@ static int sci_notifier(struct notifier_block *self, (phase == CPUFREQ_RESUMECHANGE)) { spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(sci_port, &priv->ports, node) - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); spin_unlock_irqrestore(&priv->lock, flags); } @@ -810,21 +811,17 @@ static void sci_clk_enable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - clk_enable(sci_port->dclk); - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); - - if (sci_port->iclk) - clk_enable(sci_port->iclk); + clk_enable(sci_port->iclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + clk_enable(sci_port->fclk); } static void sci_clk_disable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - if (sci_port->iclk) - clk_disable(sci_port->iclk); - - clk_disable(sci_port->dclk); + clk_disable(sci_port->fclk); + clk_disable(sci_port->iclk); } static int sci_request_irq(struct sci_port *port) @@ -913,22 +910,26 @@ static void sci_dma_tx_complete(void *arg) spin_lock_irqsave(&port->lock, flags); - xmit->tail += s->sg_tx.length; + xmit->tail += sg_dma_len(&s->sg_tx); xmit->tail &= UART_XMIT_SIZE - 1; - port->icount.tx += s->sg_tx.length; + port->icount.tx += sg_dma_len(&s->sg_tx); async_tx_ack(s->desc_tx); s->cookie_tx = -EINVAL; s->desc_tx = NULL; - spin_unlock_irqrestore(&port->lock, flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_chars_pending(xmit)) + if (!uart_circ_empty(xmit)) { schedule_work(&s->work_tx); + } else if (port->type == PORT_SCIFA) { + u16 ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); + } + + spin_unlock_irqrestore(&port->lock, flags); } /* Locking: called with port lock held */ @@ -972,13 +973,13 @@ static void sci_dma_rx_complete(void *arg) unsigned long flags; int count; - dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); spin_lock_irqsave(&port->lock, flags); count = sci_dma_rx_push(s, tty, s->buf_len_rx); - mod_timer(&s->rx_timer, jiffies + msecs_to_jiffies(5)); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); spin_unlock_irqrestore(&port->lock, flags); @@ -1050,6 +1051,8 @@ static void sci_submit_rx(struct sci_port *s) sci_rx_dma_release(s, true); return; } + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); } s->active_rx = s->cookie_rx[0]; @@ -1107,10 +1110,10 @@ static void work_fn_rx(struct work_struct *work) return; } - dev_dbg(port->dev, "%s: cookie %d #%d\n", __func__, - s->cookie_rx[new], new); - s->active_rx = s->cookie_rx[!new]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, + s->cookie_rx[new], new, s->active_rx); } static void work_fn_tx(struct work_struct *work) @@ -1131,14 +1134,13 @@ static void work_fn_tx(struct work_struct *work) */ spin_lock_irq(&port->lock); sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); - sg->dma_address = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + sg->offset; - sg->length = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); - sg->dma_length = sg->length; spin_unlock_irq(&port->lock); - BUG_ON(!sg->length); + BUG_ON(!sg_dma_len(sg)); desc = chan->device->device_prep_slave_sg(chan, sg, s->sg_len_tx, DMA_TO_DEVICE, @@ -1173,23 +1175,28 @@ static void work_fn_tx(struct work_struct *work) static void sci_start_tx(struct uart_port *port) { + struct sci_port *s = to_sci_port(port); unsigned short ctrl; #ifdef CONFIG_SERIAL_SH_SCI_DMA - struct sci_port *s = to_sci_port(port); - - if (s->chan_tx) { - if (!uart_circ_empty(&s->port.state->xmit) && s->cookie_tx < 0) - schedule_work(&s->work_tx); - - return; + if (port->type == PORT_SCIFA) { + u16 new, scr = sci_in(port, SCSCR); + if (s->chan_tx) + new = scr | 0x8000; + else + new = scr & ~0x8000; + if (new != scr) + sci_out(port, SCSCR, new); } + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + s->cookie_tx < 0) + schedule_work(&s->work_tx); #endif - - /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - ctrl |= SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); + if (!s->chan_tx || port->type == PORT_SCIFA) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); + } } static void sci_stop_tx(struct uart_port *port) @@ -1198,6 +1205,8 @@ static void sci_stop_tx(struct uart_port *port) /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x8000; ctrl &= ~SCI_CTRL_FLAGS_TIE; sci_out(port, SCSCR, ctrl); } @@ -1208,6 +1217,8 @@ static void sci_start_rx(struct uart_port *port) /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl |= sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x4000; sci_out(port, SCSCR, ctrl); } @@ -1217,6 +1228,8 @@ static void sci_stop_rx(struct uart_port *port) /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA) + ctrl &= ~0x4000; ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); sci_out(port, SCSCR, ctrl); } @@ -1251,8 +1264,12 @@ static void rx_timer_fn(unsigned long arg) { struct sci_port *s = (struct sci_port *)arg; struct uart_port *port = &s->port; - u16 scr = sci_in(port, SCSCR); + + if (port->type == PORT_SCIFA) { + scr &= ~0x4000; + enable_irq(s->irqs[1]); + } sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); dev_dbg(port->dev, "DMA Rx timed out\n"); schedule_work(&s->work_rx); @@ -1339,8 +1356,7 @@ static void sci_request_dma(struct uart_port *port) sg_init_table(sg, 1); sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, (int)buf[i] & ~PAGE_MASK); - sg->dma_address = dma[i]; - sg->dma_length = sg->length; + sg_dma_address(sg) = dma[i]; } INIT_WORK(&s->work_rx, work_fn_rx); @@ -1403,8 +1419,12 @@ static void sci_shutdown(struct uart_port *port) static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct sci_port *s = to_sci_port(port); +#endif unsigned int status, baud, smr_val, max_baud; int t = -1; + u16 scfcr = 0; /* * earlyprintk comes here early on with port->uartclk set to zero. @@ -1427,7 +1447,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ if (port->type != PORT_SCI) - sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); + sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); smr_val = sci_in(port, SCSMR) & 3; if ((termios->c_cflag & CSIZE) == CS7) @@ -1458,10 +1478,32 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, } sci_init_pins(port, termios->c_cflag); - sci_out(port, SCFCR, (termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0); + sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); sci_out(port, SCSCR, SCSCR_INIT(port)); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + /* + * Calculate delay for 1.5 DMA buffers: see + * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits + * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function + * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." + * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO + * sizes), but it has been found out experimentally, that this is not + * enough: the driver too often needlessly runs on a DMA timeout. 20ms + * as a minimum seem to work perfectly. + */ + if (s->chan_rx) { + s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / + port->fifosize / 2; + dev_dbg(port->dev, + "DMA Rx t-out %ums, tty t-out %u jiffies\n", + s->rx_timeout * 1000 / HZ, port->timeout); + if (s->rx_timeout < msecs_to_jiffies(20)) + s->rx_timeout = msecs_to_jiffies(20); + } +#endif + if ((termios->c_cflag & CREAD) != 0) sci_start_rx(port); } @@ -1553,10 +1595,10 @@ static struct uart_ops sci_uart_ops = { #endif }; -static void __devinit sci_init_single(struct platform_device *dev, - struct sci_port *sci_port, - unsigned int index, - struct plat_sci_port *p) +static int __devinit sci_init_single(struct platform_device *dev, + struct sci_port *sci_port, + unsigned int index, + struct plat_sci_port *p) { struct uart_port *port = &sci_port->port; @@ -1577,8 +1619,23 @@ static void __devinit sci_init_single(struct platform_device *dev, } if (dev) { - sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL; - sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); + sci_port->iclk = clk_get(&dev->dev, "sci_ick"); + if (IS_ERR(sci_port->iclk)) { + sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); + if (IS_ERR(sci_port->iclk)) { + dev_err(&dev->dev, "can't get iclk\n"); + return PTR_ERR(sci_port->iclk); + } + } + + /* + * The function clock is optional, ignore it if we can't + * find it. + */ + sci_port->fclk = clk_get(&dev->dev, "sci_fck"); + if (IS_ERR(sci_port->fclk)) + sci_port->fclk = NULL; + sci_port->enable = sci_clk_enable; sci_port->disable = sci_clk_disable; port->dev = &dev->dev; @@ -1605,6 +1662,7 @@ static void __devinit sci_init_single(struct platform_device *dev, #endif memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); + return 0; } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE @@ -1754,8 +1812,11 @@ static int sci_remove(struct platform_device *dev) cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(p, &priv->ports, node) + list_for_each_entry(p, &priv->ports, node) { uart_remove_one_port(&sci_uart_driver, &p->port); + clk_put(p->iclk); + clk_put(p->fclk); + } spin_unlock_irqrestore(&priv->lock, flags); kfree(priv); @@ -1781,7 +1842,9 @@ static int __devinit sci_probe_single(struct platform_device *dev, return 0; } - sci_init_single(dev, sciport, index, p); + ret = sci_init_single(dev, sciport, index, p); + if (ret) + return ret; ret = uart_add_one_port(&sci_uart_driver, &sciport->port); if (ret) diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig new file mode 100644 index 000000000000..a54de0b9b3df --- /dev/null +++ b/drivers/sh/Kconfig @@ -0,0 +1,24 @@ +config INTC_USERIMASK + bool "Userspace interrupt masking support" + depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) + help + This enables support for hardware-assisted userspace hardirq + masking. + + SH-4A and newer interrupt blocks all support a special shadowed + page with all non-masking registers obscured when mapped in to + userspace. This is primarily for use by userspace device + drivers that are using special priority levels. + + If in doubt, say N. + +config INTC_BALANCING + bool "Hardware IRQ balancing support" + depends on SMP && SUPERH && CPU_SUBTYPE_SH7786 + help + This enables support for IRQ auto-distribution mode on SH-X3 + SMP parts. All of the balancing and CPU wakeup decisions are + taken care of automatically by hardware for distributed + vectors. + + If in doubt, say N. diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 4956bf1f2134..78bb5127abd0 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -4,4 +4,6 @@ obj-$(CONFIG_SUPERHYWAY) += superhyway/ obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_GENERIC_GPIO) += pfc.o +obj-$(CONFIG_SUPERH) += clk.o +obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o obj-y += intc.o diff --git a/drivers/sh/clk-cpg.c b/drivers/sh/clk-cpg.c new file mode 100644 index 000000000000..f5c80ba9ab1c --- /dev/null +++ b/drivers/sh/clk-cpg.c @@ -0,0 +1,298 @@ +#include <linux/clk.h> +#include <linux/compiler.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/sh_clk.h> + +static int sh_clk_mstp32_enable(struct clk *clk) +{ + __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit), + clk->enable_reg); + return 0; +} + +static void sh_clk_mstp32_disable(struct clk *clk) +{ + __raw_writel(__raw_readl(clk->enable_reg) | (1 << clk->enable_bit), + clk->enable_reg); +} + +static struct clk_ops sh_clk_mstp32_clk_ops = { + .enable = sh_clk_mstp32_enable, + .disable = sh_clk_mstp32_disable, + .recalc = followparent_recalc, +}; + +int __init sh_clk_mstp32_register(struct clk *clks, int nr) +{ + struct clk *clkp; + int ret = 0; + int k; + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + clkp->ops = &sh_clk_mstp32_clk_ops; + ret |= clk_register(clkp); + } + + return ret; +} + +static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) +{ + return clk_rate_table_round(clk, clk->freq_table, rate); +} + +static int sh_clk_div6_divisors[64] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 +}; + +static struct clk_div_mult_table sh_clk_div6_table = { + .divisors = sh_clk_div6_divisors, + .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), +}; + +static unsigned long sh_clk_div6_recalc(struct clk *clk) +{ + struct clk_div_mult_table *table = &sh_clk_div6_table; + unsigned int idx; + + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, NULL); + + idx = __raw_readl(clk->enable_reg) & 0x003f; + + return clk->freq_table[idx].frequency; +} + +static int sh_clk_div6_set_rate(struct clk *clk, + unsigned long rate, int algo_id) +{ + unsigned long value; + int idx; + + idx = clk_rate_table_find(clk, clk->freq_table, rate); + if (idx < 0) + return idx; + + value = __raw_readl(clk->enable_reg); + value &= ~0x3f; + value |= idx; + __raw_writel(value, clk->enable_reg); + return 0; +} + +static int sh_clk_div6_enable(struct clk *clk) +{ + unsigned long value; + int ret; + + ret = sh_clk_div6_set_rate(clk, clk->rate, 0); + if (ret == 0) { + value = __raw_readl(clk->enable_reg); + value &= ~0x100; /* clear stop bit to enable clock */ + __raw_writel(value, clk->enable_reg); + } + return ret; +} + +static void sh_clk_div6_disable(struct clk *clk) +{ + unsigned long value; + + value = __raw_readl(clk->enable_reg); + value |= 0x100; /* stop clock */ + value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ + __raw_writel(value, clk->enable_reg); +} + +static struct clk_ops sh_clk_div6_clk_ops = { + .recalc = sh_clk_div6_recalc, + .round_rate = sh_clk_div_round_rate, + .set_rate = sh_clk_div6_set_rate, + .enable = sh_clk_div6_enable, + .disable = sh_clk_div6_disable, +}; + +int __init sh_clk_div6_register(struct clk *clks, int nr) +{ + struct clk *clkp; + void *freq_table; + int nr_divs = sh_clk_div6_table.nr_divisors; + int freq_table_size = sizeof(struct cpufreq_frequency_table); + int ret = 0; + int k; + + freq_table_size *= (nr_divs + 1); + freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); + if (!freq_table) { + pr_err("sh_clk_div6_register: unable to alloc memory\n"); + return -ENOMEM; + } + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + + clkp->ops = &sh_clk_div6_clk_ops; + clkp->id = -1; + clkp->freq_table = freq_table + (k * freq_table_size); + clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; + + ret = clk_register(clkp); + } + + return ret; +} + +static unsigned long sh_clk_div4_recalc(struct clk *clk) +{ + struct clk_div4_table *d4t = clk->priv; + struct clk_div_mult_table *table = d4t->div_mult_table; + unsigned int idx; + + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, &clk->arch_flags); + + idx = (__raw_readl(clk->enable_reg) >> clk->enable_bit) & 0x000f; + + return clk->freq_table[idx].frequency; +} + +static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_div4_table *d4t = clk->priv; + struct clk_div_mult_table *table = d4t->div_mult_table; + u32 value; + int ret; + + /* we really need a better way to determine parent index, but for + * now assume internal parent comes with CLK_ENABLE_ON_INIT set, + * no CLK_ENABLE_ON_INIT means external clock... + */ + + if (parent->flags & CLK_ENABLE_ON_INIT) + value = __raw_readl(clk->enable_reg) & ~(1 << 7); + else + value = __raw_readl(clk->enable_reg) | (1 << 7); + + ret = clk_reparent(clk, parent); + if (ret < 0) + return ret; + + __raw_writel(value, clk->enable_reg); + + /* Rebiuld the frequency table */ + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, &clk->arch_flags); + + return 0; +} + +static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate, int algo_id) +{ + struct clk_div4_table *d4t = clk->priv; + unsigned long value; + int idx = clk_rate_table_find(clk, clk->freq_table, rate); + if (idx < 0) + return idx; + + value = __raw_readl(clk->enable_reg); + value &= ~(0xf << clk->enable_bit); + value |= (idx << clk->enable_bit); + __raw_writel(value, clk->enable_reg); + + if (d4t->kick) + d4t->kick(clk); + + return 0; +} + +static int sh_clk_div4_enable(struct clk *clk) +{ + __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << 8), clk->enable_reg); + return 0; +} + +static void sh_clk_div4_disable(struct clk *clk) +{ + __raw_writel(__raw_readl(clk->enable_reg) | (1 << 8), clk->enable_reg); +} + +static struct clk_ops sh_clk_div4_clk_ops = { + .recalc = sh_clk_div4_recalc, + .set_rate = sh_clk_div4_set_rate, + .round_rate = sh_clk_div_round_rate, +}; + +static struct clk_ops sh_clk_div4_enable_clk_ops = { + .recalc = sh_clk_div4_recalc, + .set_rate = sh_clk_div4_set_rate, + .round_rate = sh_clk_div_round_rate, + .enable = sh_clk_div4_enable, + .disable = sh_clk_div4_disable, +}; + +static struct clk_ops sh_clk_div4_reparent_clk_ops = { + .recalc = sh_clk_div4_recalc, + .set_rate = sh_clk_div4_set_rate, + .round_rate = sh_clk_div_round_rate, + .enable = sh_clk_div4_enable, + .disable = sh_clk_div4_disable, + .set_parent = sh_clk_div4_set_parent, +}; + +static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, + struct clk_div4_table *table, struct clk_ops *ops) +{ + struct clk *clkp; + void *freq_table; + int nr_divs = table->div_mult_table->nr_divisors; + int freq_table_size = sizeof(struct cpufreq_frequency_table); + int ret = 0; + int k; + + freq_table_size *= (nr_divs + 1); + freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); + if (!freq_table) { + pr_err("sh_clk_div4_register: unable to alloc memory\n"); + return -ENOMEM; + } + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + + clkp->ops = ops; + clkp->id = -1; + clkp->priv = table; + + clkp->freq_table = freq_table + (k * freq_table_size); + clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; + + ret = clk_register(clkp); + } + + return ret; +} + +int __init sh_clk_div4_register(struct clk *clks, int nr, + struct clk_div4_table *table) +{ + return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops); +} + +int __init sh_clk_div4_enable_register(struct clk *clks, int nr, + struct clk_div4_table *table) +{ + return sh_clk_div4_register_ops(clks, nr, table, + &sh_clk_div4_enable_clk_ops); +} + +int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, + struct clk_div4_table *table) +{ + return sh_clk_div4_register_ops(clks, nr, table, + &sh_clk_div4_reparent_clk_ops); +} diff --git a/drivers/sh/clk.c b/drivers/sh/clk.c new file mode 100644 index 000000000000..5d84adac9ec4 --- /dev/null +++ b/drivers/sh/clk.c @@ -0,0 +1,545 @@ +/* + * drivers/sh/clk.c - SuperH clock framework + * + * Copyright (C) 2005 - 2009 Paul Mundt + * + * This clock framework is derived from the OMAP version by: + * + * Copyright (C) 2004 - 2008 Nokia Corporation + * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * + * Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com> + * + * 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 + * for more details. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/kobject.h> +#include <linux/sysdev.h> +#include <linux/seq_file.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/debugfs.h> +#include <linux/cpufreq.h> +#include <linux/clk.h> +#include <linux/sh_clk.h> + +static LIST_HEAD(clock_list); +static DEFINE_SPINLOCK(clock_lock); +static DEFINE_MUTEX(clock_list_sem); + +void clk_rate_table_build(struct clk *clk, + struct cpufreq_frequency_table *freq_table, + int nr_freqs, + struct clk_div_mult_table *src_table, + unsigned long *bitmap) +{ + unsigned long mult, div; + unsigned long freq; + int i; + + for (i = 0; i < nr_freqs; i++) { + div = 1; + mult = 1; + + if (src_table->divisors && i < src_table->nr_divisors) + div = src_table->divisors[i]; + + if (src_table->multipliers && i < src_table->nr_multipliers) + mult = src_table->multipliers[i]; + + if (!div || !mult || (bitmap && !test_bit(i, bitmap))) + freq = CPUFREQ_ENTRY_INVALID; + else + freq = clk->parent->rate * mult / div; + + freq_table[i].index = i; + freq_table[i].frequency = freq; + } + + /* Termination entry */ + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; +} + +long clk_rate_table_round(struct clk *clk, + struct cpufreq_frequency_table *freq_table, + unsigned long rate) +{ + unsigned long rate_error, rate_error_prev = ~0UL; + unsigned long rate_best_fit = rate; + unsigned long highest, lowest; + int i; + + highest = lowest = 0; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned long freq = freq_table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + + if (freq > highest) + highest = freq; + if (freq < lowest) + lowest = freq; + + rate_error = abs(freq - rate); + if (rate_error < rate_error_prev) { + rate_best_fit = freq; + rate_error_prev = rate_error; + } + + if (rate_error == 0) + break; + } + + if (rate >= highest) + rate_best_fit = highest; + if (rate <= lowest) + rate_best_fit = lowest; + + return rate_best_fit; +} + +int clk_rate_table_find(struct clk *clk, + struct cpufreq_frequency_table *freq_table, + unsigned long rate) +{ + int i; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned long freq = freq_table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + + if (freq == rate) + return i; + } + + return -ENOENT; +} + +/* Used for clocks that always have same value as the parent clock */ +unsigned long followparent_recalc(struct clk *clk) +{ + return clk->parent ? clk->parent->rate : 0; +} + +int clk_reparent(struct clk *child, struct clk *parent) +{ + list_del_init(&child->sibling); + if (parent) + list_add(&child->sibling, &parent->children); + child->parent = parent; + + /* now do the debugfs renaming to reattach the child + to the proper parent */ + + return 0; +} + +/* Propagate rate to children */ +void propagate_rate(struct clk *tclk) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &tclk->children, sibling) { + if (clkp->ops && clkp->ops->recalc) + clkp->rate = clkp->ops->recalc(clkp); + + propagate_rate(clkp); + } +} + +static void __clk_disable(struct clk *clk) +{ + if (WARN(!clk->usecount, "Trying to disable clock %s with 0 usecount\n", + clk->name)) + return; + + if (!(--clk->usecount)) { + if (likely(clk->ops && clk->ops->disable)) + clk->ops->disable(clk); + if (likely(clk->parent)) + __clk_disable(clk->parent); + } +} + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (!clk) + return; + + spin_lock_irqsave(&clock_lock, flags); + __clk_disable(clk); + spin_unlock_irqrestore(&clock_lock, flags); +} +EXPORT_SYMBOL_GPL(clk_disable); + +static int __clk_enable(struct clk *clk) +{ + int ret = 0; + + if (clk->usecount++ == 0) { + if (clk->parent) { + ret = __clk_enable(clk->parent); + if (unlikely(ret)) + goto err; + } + + if (clk->ops && clk->ops->enable) { + ret = clk->ops->enable(clk); + if (ret) { + if (clk->parent) + __clk_disable(clk->parent); + goto err; + } + } + } + + return ret; +err: + clk->usecount--; + return ret; +} + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int ret; + + if (!clk) + return -EINVAL; + + spin_lock_irqsave(&clock_lock, flags); + ret = __clk_enable(clk); + spin_unlock_irqrestore(&clock_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_enable); + +static LIST_HEAD(root_clks); + +/** + * recalculate_root_clocks - recalculate and propagate all root clocks + * + * Recalculates all root clocks (clocks with no parent), which if the + * clock's .recalc is set correctly, should also propagate their rates. + * Called at init. + */ +void recalculate_root_clocks(void) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &root_clks, sibling) { + if (clkp->ops && clkp->ops->recalc) + clkp->rate = clkp->ops->recalc(clkp); + propagate_rate(clkp); + } +} + +int clk_register(struct clk *clk) +{ + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + + /* + * trap out already registered clocks + */ + if (clk->node.next || clk->node.prev) + return 0; + + mutex_lock(&clock_list_sem); + + INIT_LIST_HEAD(&clk->children); + clk->usecount = 0; + + if (clk->parent) + list_add(&clk->sibling, &clk->parent->children); + else + list_add(&clk->sibling, &root_clks); + + list_add(&clk->node, &clock_list); + if (clk->ops && clk->ops->init) + clk->ops->init(clk); + mutex_unlock(&clock_list_sem); + + return 0; +} +EXPORT_SYMBOL_GPL(clk_register); + +void clk_unregister(struct clk *clk) +{ + mutex_lock(&clock_list_sem); + list_del(&clk->sibling); + list_del(&clk->node); + mutex_unlock(&clock_list_sem); +} +EXPORT_SYMBOL_GPL(clk_unregister); + +void clk_enable_init_clocks(void) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &clock_list, node) + if (clkp->flags & CLK_ENABLE_ON_INIT) + clk_enable(clkp); +} + +unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} +EXPORT_SYMBOL_GPL(clk_get_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return clk_set_rate_ex(clk, rate, 0); +} +EXPORT_SYMBOL_GPL(clk_set_rate); + +int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id) +{ + int ret = -EOPNOTSUPP; + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + + if (likely(clk->ops && clk->ops->set_rate)) { + ret = clk->ops->set_rate(clk, rate, algo_id); + if (ret != 0) + goto out_unlock; + } else { + clk->rate = rate; + ret = 0; + } + + if (clk->ops && clk->ops->recalc) + clk->rate = clk->ops->recalc(clk); + + propagate_rate(clk); + +out_unlock: + spin_unlock_irqrestore(&clock_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_set_rate_ex); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + unsigned long flags; + int ret = -EINVAL; + + if (!parent || !clk) + return ret; + if (clk->parent == parent) + return 0; + + spin_lock_irqsave(&clock_lock, flags); + if (clk->usecount == 0) { + if (clk->ops->set_parent) + ret = clk->ops->set_parent(clk, parent); + else + ret = clk_reparent(clk, parent); + + if (ret == 0) { + pr_debug("clock: set parent of %s to %s (new rate %ld)\n", + clk->name, clk->parent->name, clk->rate); + if (clk->ops->recalc) + clk->rate = clk->ops->recalc(clk); + propagate_rate(clk); + } + } else + ret = -EBUSY; + spin_unlock_irqrestore(&clock_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_set_parent); + +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} +EXPORT_SYMBOL_GPL(clk_get_parent); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (likely(clk->ops && clk->ops->round_rate)) { + unsigned long flags, rounded; + + spin_lock_irqsave(&clock_lock, flags); + rounded = clk->ops->round_rate(clk, rate); + spin_unlock_irqrestore(&clock_lock, flags); + + return rounded; + } + + return clk_get_rate(clk); +} +EXPORT_SYMBOL_GPL(clk_round_rate); + +#ifdef CONFIG_PM +static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) +{ + static pm_message_t prev_state; + struct clk *clkp; + + switch (state.event) { + case PM_EVENT_ON: + /* Resumeing from hibernation */ + if (prev_state.event != PM_EVENT_FREEZE) + break; + + list_for_each_entry(clkp, &clock_list, node) { + if (likely(clkp->ops)) { + unsigned long rate = clkp->rate; + + if (likely(clkp->ops->set_parent)) + clkp->ops->set_parent(clkp, + clkp->parent); + if (likely(clkp->ops->set_rate)) + clkp->ops->set_rate(clkp, + rate, NO_CHANGE); + else if (likely(clkp->ops->recalc)) + clkp->rate = clkp->ops->recalc(clkp); + } + } + break; + case PM_EVENT_FREEZE: + break; + case PM_EVENT_SUSPEND: + break; + } + + prev_state = state; + return 0; +} + +static int clks_sysdev_resume(struct sys_device *dev) +{ + return clks_sysdev_suspend(dev, PMSG_ON); +} + +static struct sysdev_class clks_sysdev_class = { + .name = "clks", +}; + +static struct sysdev_driver clks_sysdev_driver = { + .suspend = clks_sysdev_suspend, + .resume = clks_sysdev_resume, +}; + +static struct sys_device clks_sysdev_dev = { + .cls = &clks_sysdev_class, +}; + +static int __init clk_sysdev_init(void) +{ + sysdev_class_register(&clks_sysdev_class); + sysdev_driver_register(&clks_sysdev_class, &clks_sysdev_driver); + sysdev_register(&clks_sysdev_dev); + + return 0; +} +subsys_initcall(clk_sysdev_init); +#endif + +/* + * debugfs support to trace clock tree hierarchy and attributes + */ +static struct dentry *clk_debugfs_root; + +static int clk_debugfs_register_one(struct clk *c) +{ + int err; + struct dentry *d, *child, *child_tmp; + struct clk *pa = c->parent; + char s[255]; + char *p = s; + + p += sprintf(p, "%s", c->name); + if (c->id >= 0) + sprintf(p, ":%d", c->id); + d = debugfs_create_dir(s, pa ? pa->dentry : clk_debugfs_root); + if (!d) + return -ENOMEM; + c->dentry = d; + + d = debugfs_create_u8("usecount", S_IRUGO, c->dentry, (u8 *)&c->usecount); + if (!d) { + err = -ENOMEM; + goto err_out; + } + d = debugfs_create_u32("rate", S_IRUGO, c->dentry, (u32 *)&c->rate); + if (!d) { + err = -ENOMEM; + goto err_out; + } + d = debugfs_create_x32("flags", S_IRUGO, c->dentry, (u32 *)&c->flags); + if (!d) { + err = -ENOMEM; + goto err_out; + } + return 0; + +err_out: + d = c->dentry; + list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child) + debugfs_remove(child); + debugfs_remove(c->dentry); + return err; +} + +static int clk_debugfs_register(struct clk *c) +{ + int err; + struct clk *pa = c->parent; + + if (pa && !pa->dentry) { + err = clk_debugfs_register(pa); + if (err) + return err; + } + + if (!c->dentry && c->name) { + err = clk_debugfs_register_one(c); + if (err) + return err; + } + return 0; +} + +static int __init clk_debugfs_init(void) +{ + struct clk *c; + struct dentry *d; + int err; + + d = debugfs_create_dir("clock", NULL); + if (!d) + return -ENOMEM; + clk_debugfs_root = d; + + list_for_each_entry(c, &clock_list, node) { + err = clk_debugfs_register(c); + if (err) + goto err_out; + } + return 0; +err_out: + debugfs_remove_recursive(clk_debugfs_root); + return err; +} +late_initcall(clk_debugfs_init); diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 94ad6bd86a00..c585574b9aed 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -28,6 +28,7 @@ #include <linux/topology.h> #include <linux/bitmap.h> #include <linux/cpumask.h> +#include <asm/sizes.h> #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ @@ -45,6 +46,12 @@ struct intc_handle_int { unsigned long handle; }; +struct intc_window { + phys_addr_t phys; + void __iomem *virt; + unsigned long size; +}; + struct intc_desc_int { struct list_head list; struct sys_device sysdev; @@ -58,6 +65,8 @@ struct intc_desc_int { unsigned int nr_prio; struct intc_handle_int *sense; unsigned int nr_sense; + struct intc_window *window; + unsigned int nr_windows; struct irq_chip chip; }; @@ -87,8 +96,12 @@ static DEFINE_SPINLOCK(vector_lock); #define SMP_NR(d, x) 1 #endif -static unsigned int intc_prio_level[NR_IRQS]; /* for now */ +static unsigned int intc_prio_level[NR_IRQS]; /* for now */ +static unsigned int default_prio_level = 2; /* 2 - 16 */ static unsigned long ack_handle[NR_IRQS]; +#ifdef CONFIG_INTC_BALANCING +static unsigned long dist_handle[NR_IRQS]; +#endif static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { @@ -96,6 +109,47 @@ static inline struct intc_desc_int *get_intc_desc(unsigned int irq) return container_of(chip, struct intc_desc_int, chip); } +static unsigned long intc_phys_to_virt(struct intc_desc_int *d, + unsigned long address) +{ + struct intc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < d->nr_windows; k++) { + window = d->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + address -= window->phys; + address += (unsigned long)window->virt; + + return address; + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return address; +} + +static unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address) +{ + unsigned int k; + + address = intc_phys_to_virt(d, address); + + for (k = 0; k < d->nr_reg; k++) { + if (d->reg[k] == address) + return k; + } + + BUG(); + return 0; +} + static inline unsigned int set_field(unsigned int value, unsigned int field_value, unsigned int handle) @@ -229,6 +283,85 @@ static void (*intc_disable_fns[])(unsigned long addr, [MODE_PCLR_REG] = intc_mode_field, }; +#ifdef CONFIG_INTC_BALANCING +static inline void intc_balancing_enable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = dist_handle[irq]; + unsigned long addr; + + if (irq_balancing_disabled(irq) || !handle) + return; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + intc_reg_fns[_INTC_FN(handle)](addr, handle, 1); +} + +static inline void intc_balancing_disable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = dist_handle[irq]; + unsigned long addr; + + if (irq_balancing_disabled(irq) || !handle) + return; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + intc_reg_fns[_INTC_FN(handle)](addr, handle, 0); +} + +static unsigned int intc_dist_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_mask_reg *mr = desc->hw.mask_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) { + mr = desc->hw.mask_regs + i; + + /* + * Skip this entry if there's no auto-distribution + * register associated with it. + */ + if (!mr->dist_reg) + continue; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + mode = MODE_ENABLE_REG; + reg_e = mr->dist_reg; + reg_d = mr->dist_reg; + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + /* + * It's possible we've gotten here with no distribution options + * available for the IRQ in question, so we just skip over those. + */ + return 0; +} +#else +static inline void intc_balancing_enable(unsigned int irq) +{ +} + +static inline void intc_balancing_disable(unsigned int irq) +{ +} +#endif + static inline void _intc_enable(unsigned int irq, unsigned long handle) { struct intc_desc_int *d = get_intc_desc(irq); @@ -244,6 +377,8 @@ static inline void _intc_enable(unsigned int irq, unsigned long handle) intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ [_INTC_FN(handle)], irq); } + + intc_balancing_enable(irq); } static void intc_enable(unsigned int irq) @@ -254,10 +389,12 @@ static void intc_enable(unsigned int irq) static void intc_disable(unsigned int irq) { struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = (unsigned long) get_irq_chip_data(irq); + unsigned long handle = (unsigned long)get_irq_chip_data(irq); unsigned long addr; unsigned int cpu; + intc_balancing_disable(irq); + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { #ifdef CONFIG_SMP if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) @@ -336,8 +473,7 @@ static void intc_mask_ack(unsigned int irq) intc_disable(irq); - /* read register and write zero only to the assocaited bit */ - + /* read register and write zero only to the associated bit */ if (handle) { addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); switch (_INTC_FN(handle)) { @@ -366,7 +502,8 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, { int i; - /* this doesn't scale well, but... + /* + * this doesn't scale well, but... * * this function should only be used for cerain uncommon * operations such as intc_set_priority() and intc_set_sense() @@ -377,7 +514,6 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, * memory footprint down is to make sure the array is sorted * and then perform a bisect to lookup the irq. */ - for (i = 0; i < nr_hp; i++) { if ((hp + i)->irq != irq) continue; @@ -408,7 +544,6 @@ int intc_set_priority(unsigned int irq, unsigned int prio) * primary masking method is using intc_prio_level[irq] * priority level will be set during next enable() */ - if (_INTC_FN(ihp->handle) != REG_FN_ERR) _intc_enable(irq, ihp->handle); } @@ -447,20 +582,6 @@ static int intc_set_sense(unsigned int irq, unsigned int type) return 0; } -static unsigned int __init intc_get_reg(struct intc_desc_int *d, - unsigned long address) -{ - unsigned int k; - - for (k = 0; k < d->nr_reg; k++) { - if (d->reg[k] == address) - return k; - } - - BUG(); - return 0; -} - static intc_enum __init intc_grp_id(struct intc_desc *desc, intc_enum enum_id) { @@ -718,13 +839,14 @@ static void __init intc_register_irq(struct intc_desc *desc, */ set_bit(irq, intc_irq_map); - /* Prefer single interrupt source bitmap over other combinations: + /* + * Prefer single interrupt source bitmap over other combinations: + * * 1. bitmap, single interrupt source * 2. priority, single interrupt source * 3. bitmap, multiple interrupt sources (groups) * 4. priority, multiple interrupt sources (groups) */ - data[0] = intc_mask_data(desc, d, enum_id, 0); data[1] = intc_prio_data(desc, d, enum_id, 0); @@ -749,10 +871,11 @@ static void __init intc_register_irq(struct intc_desc *desc, handle_level_irq, "level"); set_irq_chip_data(irq, (void *)data[primary]); - /* set priority level + /* + * set priority level * - this needs to be at least 2 for 5-bit priorities on 7780 */ - intc_prio_level[irq] = 2; + intc_prio_level[irq] = default_prio_level; /* enable secondary masking method if present */ if (data[!primary]) @@ -769,7 +892,6 @@ static void __init intc_register_irq(struct intc_desc *desc, * only secondary priority should access registers, so * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() */ - hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); } @@ -790,6 +912,11 @@ static void __init intc_register_irq(struct intc_desc *desc, if (desc->hw.ack_regs) ack_handle[irq] = intc_ack_data(desc, d, enum_id); +#ifdef CONFIG_INTC_BALANCING + if (desc->hw.mask_regs) + dist_handle[irq] = intc_dist_data(desc, d, enum_id); +#endif + #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ #endif @@ -801,6 +928,8 @@ static unsigned int __init save_reg(struct intc_desc_int *d, unsigned int smp) { if (value) { + value = intc_phys_to_virt(d, value); + d->reg[cnt] = value; #ifdef CONFIG_SMP d->smp[cnt] = smp; @@ -816,25 +945,59 @@ static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) generic_handle_irq((unsigned int)get_irq_data(irq)); } -void __init register_intc_controller(struct intc_desc *desc) +int __init register_intc_controller(struct intc_desc *desc) { unsigned int i, k, smp; struct intc_hw_desc *hw = &desc->hw; struct intc_desc_int *d; + struct resource *res; + + pr_info("intc: Registered controller '%s' with %u IRQs\n", + desc->name, hw->nr_vectors); d = kzalloc(sizeof(*d), GFP_NOWAIT); + if (!d) + goto err0; INIT_LIST_HEAD(&d->list); list_add(&d->list, &intc_list); + if (desc->num_resources) { + d->nr_windows = desc->num_resources; + d->window = kzalloc(d->nr_windows * sizeof(*d->window), + GFP_NOWAIT); + if (!d->window) + goto err1; + + for (k = 0; k < d->nr_windows; k++) { + res = desc->resource + k; + WARN_ON(resource_type(res) != IORESOURCE_MEM); + d->window[k].phys = res->start; + d->window[k].size = resource_size(res); + d->window[k].virt = ioremap_nocache(res->start, + resource_size(res)); + if (!d->window[k].virt) + goto err2; + } + } + d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; +#ifdef CONFIG_INTC_BALANCING + if (d->nr_reg) + d->nr_reg += hw->nr_mask_regs; +#endif d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); + if (!d->reg) + goto err2; + #ifdef CONFIG_SMP d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); + if (!d->smp) + goto err3; #endif k = 0; @@ -843,12 +1006,17 @@ void __init register_intc_controller(struct intc_desc *desc) smp = IS_SMP(hw->mask_regs[i]); k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); +#ifdef CONFIG_INTC_BALANCING + k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); +#endif } } if (hw->prio_regs) { d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), GFP_NOWAIT); + if (!d->prio) + goto err4; for (i = 0; i < hw->nr_prio_regs; i++) { smp = IS_SMP(hw->prio_regs[i]); @@ -860,6 +1028,8 @@ void __init register_intc_controller(struct intc_desc *desc) if (hw->sense_regs) { d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), GFP_NOWAIT); + if (!d->sense) + goto err5; for (i = 0; i < hw->nr_sense_regs; i++) k += save_reg(d, k, hw->sense_regs[i].reg, 0); @@ -906,7 +1076,7 @@ void __init register_intc_controller(struct intc_desc *desc) irq_desc = irq_to_desc_alloc_node(irq, numa_node_id()); if (unlikely(!irq_desc)) { - pr_info("can't get irq_desc for %d\n", irq); + pr_err("can't get irq_desc for %d\n", irq); continue; } @@ -926,7 +1096,7 @@ void __init register_intc_controller(struct intc_desc *desc) */ irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id()); if (unlikely(!irq_desc)) { - pr_info("can't get irq_desc for %d\n", irq2); + pr_err("can't get irq_desc for %d\n", irq2); continue; } @@ -942,8 +1112,100 @@ void __init register_intc_controller(struct intc_desc *desc) /* enable bits matching force_enable after registering irqs */ if (desc->force_enable) intc_enable_disable_enum(desc, d, desc->force_enable, 1); + + return 0; +err5: + kfree(d->prio); +err4: +#ifdef CONFIG_SMP + kfree(d->smp); +err3: +#endif + kfree(d->reg); +err2: + for (k = 0; k < d->nr_windows; k++) + if (d->window[k].virt) + iounmap(d->window[k].virt); + + kfree(d->window); +err1: + kfree(d); +err0: + pr_err("unable to allocate INTC memory\n"); + + return -ENOMEM; +} + +#ifdef CONFIG_INTC_USERIMASK +static void __iomem *uimask; + +int register_intc_userimask(unsigned long addr) +{ + if (unlikely(uimask)) + return -EBUSY; + + uimask = ioremap_nocache(addr, SZ_4K); + if (unlikely(!uimask)) + return -ENOMEM; + + pr_info("intc: userimask support registered for levels 0 -> %d\n", + default_prio_level - 1); + + return 0; +} + +static ssize_t +show_intc_userimask(struct sysdev_class *cls, + struct sysdev_class_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf); +} + +static ssize_t +store_intc_userimask(struct sysdev_class *cls, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) +{ + unsigned long level; + + level = simple_strtoul(buf, NULL, 10); + + /* + * Minimal acceptable IRQ levels are in the 2 - 16 range, but + * these are chomped so as to not interfere with normal IRQs. + * + * Level 1 is a special case on some CPUs in that it's not + * directly settable, but given that USERIMASK cuts off below a + * certain level, we don't care about this limitation here. + * Level 0 on the other hand equates to user masking disabled. + * + * We use default_prio_level as a cut off so that only special + * case opt-in IRQs can be mangled. + */ + if (level >= default_prio_level) + return -EINVAL; + + __raw_writel(0xa5 << 24 | level << 4, uimask); + + return count; } +static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, + show_intc_userimask, store_intc_userimask); +#endif + +static ssize_t +show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) +{ + struct intc_desc_int *d; + + d = container_of(dev, struct intc_desc_int, sysdev); + + return sprintf(buf, "%s\n", d->chip.name); +} + +static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); + static int intc_suspend(struct sys_device *dev, pm_message_t state) { struct intc_desc_int *d; @@ -1003,19 +1265,28 @@ static int __init register_intc_sysdevs(void) int id = 0; error = sysdev_class_register(&intc_sysdev_class); +#ifdef CONFIG_INTC_USERIMASK + if (!error && uimask) + error = sysdev_class_create_file(&intc_sysdev_class, + &attr_userimask); +#endif if (!error) { list_for_each_entry(d, &intc_list, list) { d->sysdev.id = id; d->sysdev.cls = &intc_sysdev_class; error = sysdev_register(&d->sysdev); + if (error == 0) + error = sysdev_create_file(&d->sysdev, + &attr_name); if (error) break; + id++; } } if (error) - pr_warning("intc: sysdev registration error\n"); + pr_err("intc: sysdev registration error\n"); return error; } @@ -1048,7 +1319,7 @@ unsigned int create_irq_nr(unsigned int irq_want, int node) desc = irq_to_desc_alloc_node(new, node); if (unlikely(!desc)) { - pr_info("can't get irq_desc for %d\n", new); + pr_err("can't get irq_desc for %d\n", new); goto out_unlock; } diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 36828358a4d8..e76b1afafe07 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -36,8 +36,7 @@ #include <asm/delay.h> #include <mach/dma.h> -#include <mach/regs-ssp.h> -#include <mach/ssp.h> +#include <plat/ssp.h> #include <mach/pxa2xx_spi.h> MODULE_AUTHOR("Stephen Street"); @@ -1318,14 +1317,14 @@ static int setup(struct spi_device *spi) /* NOTE: PXA25x_SSP _could_ use external clocking ... */ if (drv_data->ssp_type != PXA25x_SSP) dev_dbg(&spi->dev, "%ld Hz actual, %s\n", - clk_get_rate(ssp->clk) - / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), - chip->enable_dma ? "DMA" : "PIO"); + clk_get_rate(ssp->clk) + / (1 + ((chip->cr0 & SSCR0_SCR(0xfff)) >> 8)), + chip->enable_dma ? "DMA" : "PIO"); else dev_dbg(&spi->dev, "%ld Hz actual, %s\n", - clk_get_rate(ssp->clk) / 2 - / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), - chip->enable_dma ? "DMA" : "PIO"); + clk_get_rate(ssp->clk) / 2 + / (1 + ((chip->cr0 & SSCR0_SCR(0x0ff)) >> 8)), + chip->enable_dma ? "DMA" : "PIO"); if (spi->bits_per_word <= 8) { chip->n_bytes = 1; @@ -1466,7 +1465,7 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) platform_info = dev->platform_data; - ssp = ssp_request(pdev->id, pdev->name); + ssp = pxa_ssp_request(pdev->id, pdev->name); if (ssp == NULL) { dev_err(&pdev->dev, "failed to request SSP%d\n", pdev->id); return -ENODEV; @@ -1476,7 +1475,7 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) master = spi_alloc_master(dev, sizeof(struct driver_data) + 16); if (!master) { dev_err(&pdev->dev, "cannot alloc spi_master\n"); - ssp_free(ssp); + pxa_ssp_free(ssp); return -ENOMEM; } drv_data = spi_master_get_devdata(master); @@ -1558,7 +1557,7 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) write_SSCR1(SSCR1_RxTresh(RX_THRESH_DFLT) | SSCR1_TxTresh(TX_THRESH_DFLT), drv_data->ioaddr); - write_SSCR0(SSCR0_SerClkDiv(2) + write_SSCR0(SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8), drv_data->ioaddr); @@ -1605,7 +1604,7 @@ out_error_irq_alloc: out_error_master_alloc: spi_master_put(master); - ssp_free(ssp); + pxa_ssp_free(ssp); return status; } @@ -1649,7 +1648,7 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) free_irq(ssp->irq, drv_data); /* Release SSP */ - ssp_free(ssp); + pxa_ssp_free(ssp); /* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c index 14d052316502..e324627d97a2 100644 --- a/drivers/spi/spi_mpc8xxx.c +++ b/drivers/spi/spi_mpc8xxx.c @@ -640,7 +640,7 @@ static int mpc8xxx_spi_setup(struct spi_device *spi) } mpc8xxx_spi = spi_master_get_devdata(spi->master); - hw_mode = cs->hw_mode; /* Save orginal settings */ + hw_mode = cs->hw_mode; /* Save original settings */ cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode); /* mask out bits we are going to set */ cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 80ff7d9e60de..bc9bdb277bec 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -490,7 +490,7 @@ static int ssb_devices_register(struct ssb_bus *bus) break; case SSB_BUSTYPE_PCMCIA: #ifdef CONFIG_SSB_PCMCIAHOST - sdev->irq = bus->host_pcmcia->irq.AssignedIRQ; + sdev->irq = bus->host_pcmcia->irq; dev->parent = &bus->host_pcmcia->dev; #endif break; diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 7696a664f8a5..5589616082e7 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -49,6 +49,8 @@ source "drivers/staging/go7007/Kconfig" source "drivers/staging/cx25821/Kconfig" +source "drivers/staging/tm6000/Kconfig" + source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ea2e70e2fed4..ec45d4bb8c11 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_ET131X) += et131x/ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ +obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c index 5632991760af..30b522c0bf2c 100644 --- a/drivers/staging/comedi/drivers/cb_das16_cs.c +++ b/drivers/staging/comedi/drivers/cb_das16_cs.c @@ -180,12 +180,12 @@ static int das16cs_attach(struct comedi_device *dev, } printk("\n"); - ret = request_irq(link->irq.AssignedIRQ, das16cs_interrupt, + ret = request_irq(link->irq, das16cs_interrupt, IRQF_SHARED, "cb_das16_cs", dev); if (ret < 0) { return ret; } - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; printk("irq=%u ", dev->irq); dev->board_ptr = das16cs_probe(dev, link); @@ -671,7 +671,6 @@ static dev_info_t dev_info = "cb_das16_cs"; struct local_info_t { struct pcmcia_device *link; - dev_node_t node; int stop; struct bus_operations *bus; }; @@ -702,10 +701,6 @@ static int das16cs_pcmcia_attach(struct pcmcia_device *link) link->priv = local; /* Initialize the pcmcia_device structure */ - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = NULL; - link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; @@ -720,10 +715,8 @@ static void das16cs_pcmcia_detach(struct pcmcia_device *link) { dev_dbg(&link->dev, "das16cs_pcmcia_detach\n"); - if (link->dev_node) { - ((struct local_info_t *)link->priv)->stop = 1; - das16cs_pcmcia_release(link); - } + ((struct local_info_t *)link->priv)->stop = 1; + das16cs_pcmcia_release(link); /* This points to the parent struct local_info_t struct */ if (link->priv) kfree(link->priv); @@ -740,8 +733,7 @@ static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev, return -EINVAL; /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -769,7 +761,6 @@ static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev, static void das16cs_pcmcia_config(struct pcmcia_device *link) { - struct local_info_t *dev = link->priv; int ret; dev_dbg(&link->dev, "das16cs_pcmcia_config\n"); @@ -780,16 +771,9 @@ static void das16cs_pcmcia_config(struct pcmcia_device *link) goto failed; } - /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } + if (!link->irq) + goto failed; + /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping, and putting the @@ -799,19 +783,10 @@ static void das16cs_pcmcia_config(struct pcmcia_device *link) if (ret) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - sprintf(dev->node.dev_name, "cb_das16_cs"); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x", link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %u", link->irq.AssignedIRQ); + printk(", irq %u", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1 + link->io.NumPorts1 - 1); diff --git a/drivers/staging/comedi/drivers/das08_cs.c b/drivers/staging/comedi/drivers/das08_cs.c index 9164ce158dcd..896d25bc85b5 100644 --- a/drivers/staging/comedi/drivers/das08_cs.c +++ b/drivers/staging/comedi/drivers/das08_cs.c @@ -142,7 +142,6 @@ static const dev_info_t dev_info = "pcm-das08"; struct local_info_t { struct pcmcia_device *link; - dev_node_t node; int stop; struct bus_operations *bus; }; @@ -172,10 +171,6 @@ static int das08_pcmcia_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -207,10 +202,8 @@ static void das08_pcmcia_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "das08_pcmcia_detach\n"); - if (link->dev_node) { - ((struct local_info_t *)link->priv)->stop = 1; - das08_pcmcia_release(link); - } + ((struct local_info_t *)link->priv)->stop = 1; + das08_pcmcia_release(link); /* This points to the parent struct local_info_t struct */ if (link->priv) @@ -229,8 +222,7 @@ static int das08_pcmcia_config_loop(struct pcmcia_device *p_dev, return -ENODEV; /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -266,7 +258,6 @@ static int das08_pcmcia_config_loop(struct pcmcia_device *p_dev, static void das08_pcmcia_config(struct pcmcia_device *link) { - struct local_info_t *dev = link->priv; int ret; dev_dbg(&link->dev, "das08_pcmcia_config\n"); @@ -277,11 +268,8 @@ static void das08_pcmcia_config(struct pcmcia_device *link) goto failed; } - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } + if (!link->irq) + goto failed; /* This actually configures the PCMCIA socket -- setting up @@ -292,19 +280,10 @@ static void das08_pcmcia_config(struct pcmcia_device *link) if (ret) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - sprintf(dev->node.dev_name, "pcm-das08"); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x", link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %u", link->irq.AssignedIRQ); + printk(", irq %u", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1 + link->io.NumPorts1 - 1); diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c index 7ea64538e055..06dd44ff1a95 100644 --- a/drivers/staging/comedi/drivers/ni_daq_700.c +++ b/drivers/staging/comedi/drivers/ni_daq_700.c @@ -380,7 +380,7 @@ static int dio700_attach(struct comedi_device *dev, struct comedi_devconfig *it) return -EIO; iobase = link->io.BasePort1; #ifdef incomplete - irq = link->irq.AssignedIRQ; + irq = link->irq; #endif break; default: @@ -470,7 +470,6 @@ static const dev_info_t dev_info = "ni_daq_700"; struct local_info_t { struct pcmcia_device *link; - dev_node_t node; int stop; struct bus_operations *bus; }; @@ -502,10 +501,6 @@ static int dio700_cs_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -539,10 +534,8 @@ static void dio700_cs_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "dio700_cs_detach\n"); - if (link->dev_node) { - ((struct local_info_t *)link->priv)->stop = 1; - dio700_release(link); - } + ((struct local_info_t *)link->priv)->stop = 1; + dio700_release(link); /* This points to the parent struct local_info_t struct */ if (link->priv) @@ -577,8 +570,7 @@ static int dio700_pcmcia_config_loop(struct pcmcia_device *p_dev, } /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -625,7 +617,6 @@ static int dio700_pcmcia_config_loop(struct pcmcia_device *p_dev, static void dio700_config(struct pcmcia_device *link) { - struct local_info_t *dev = link->priv; win_req_t req; int ret; @@ -639,16 +630,8 @@ static void dio700_config(struct pcmcia_device *link) goto failed; } - /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } + if (!link->irq) + goto failed; /* This actually configures the PCMCIA socket -- setting up @@ -659,19 +642,10 @@ static void dio700_config(struct pcmcia_device *link) if (ret != 0) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - sprintf(dev->node.dev_name, "ni_daq_700"); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x", link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1 + link->io.NumPorts1 - 1); diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c index ddc312b5d20d..7bfe08b01fe9 100644 --- a/drivers/staging/comedi/drivers/ni_daq_dio24.c +++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c @@ -131,7 +131,7 @@ static int dio24_attach(struct comedi_device *dev, struct comedi_devconfig *it) return -EIO; iobase = link->io.BasePort1; #ifdef incomplete - irq = link->irq.AssignedIRQ; + irq = link->irq; #endif break; default: @@ -221,7 +221,6 @@ static const dev_info_t dev_info = "ni_daq_dio24"; struct local_info_t { struct pcmcia_device *link; - dev_node_t node; int stop; struct bus_operations *bus; }; @@ -253,10 +252,6 @@ static int dio24_cs_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -290,10 +285,8 @@ static void dio24_cs_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "dio24_cs_detach\n"); - if (link->dev_node) { - ((struct local_info_t *)link->priv)->stop = 1; - dio24_release(link); - } + ((struct local_info_t *)link->priv)->stop = 1; + dio24_release(link); /* This points to the parent local_info_t struct */ if (link->priv) @@ -328,8 +321,7 @@ static int dio24_pcmcia_config_loop(struct pcmcia_device *p_dev, } /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -376,7 +368,6 @@ static int dio24_pcmcia_config_loop(struct pcmcia_device *p_dev, static void dio24_config(struct pcmcia_device *link) { - struct local_info_t *dev = link->priv; int ret; win_req_t req; @@ -390,16 +381,8 @@ static void dio24_config(struct pcmcia_device *link) goto failed; } - /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } + if (!link->irq) + goto failed; /* This actually configures the PCMCIA socket -- setting up @@ -410,19 +393,10 @@ static void dio24_config(struct pcmcia_device *link) if (ret) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - sprintf(dev->node.dev_name, "ni_daq_dio24"); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x", link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1 + link->io.NumPorts1 - 1); diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c index 8ad1055a5cc1..fd8d3e9520a0 100644 --- a/drivers/staging/comedi/drivers/ni_labpc_cs.c +++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c @@ -144,7 +144,7 @@ static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!link) return -EIO; iobase = link->io.BasePort1; - irq = link->irq.AssignedIRQ; + irq = link->irq; break; default: printk("bug! couldn't determine board type\n"); @@ -199,7 +199,6 @@ static const dev_info_t dev_info = "daqcard-1200"; struct local_info_t { struct pcmcia_device *link; - dev_node_t node; int stop; struct bus_operations *bus; }; @@ -229,10 +228,6 @@ static int labpc_cs_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FORCED_PULSE; - link->irq.Handler = NULL; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -269,10 +264,8 @@ static void labpc_cs_detach(struct pcmcia_device *link) the release() function is called, that will trigger a proper detach(). */ - if (link->dev_node) { - ((struct local_info_t *)link->priv)->stop = 1; - labpc_release(link); - } + ((struct local_info_t *)link->priv)->stop = 1; + labpc_release(link); /* This points to the parent local_info_t struct (may be null) */ kfree(link->priv); @@ -306,8 +299,7 @@ static int labpc_pcmcia_config_loop(struct pcmcia_device *p_dev, } /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -355,7 +347,6 @@ static int labpc_pcmcia_config_loop(struct pcmcia_device *p_dev, static void labpc_config(struct pcmcia_device *link) { - struct local_info_t *dev = link->priv; int ret; win_req_t req; @@ -367,16 +358,8 @@ static void labpc_config(struct pcmcia_device *link) goto failed; } - /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } + if (!link->irq) + goto failed; /* This actually configures the PCMCIA socket -- setting up @@ -387,19 +370,10 @@ static void labpc_config(struct pcmcia_device *link) if (ret) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - sprintf(dev->node.dev_name, "daqcard-1200"); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x", link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1 + link->io.NumPorts1 - 1); diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c index dc4849a40c97..1e8aebae8ae8 100644 --- a/drivers/staging/comedi/drivers/ni_mio_cs.c +++ b/drivers/staging/comedi/drivers/ni_mio_cs.c @@ -262,17 +262,11 @@ static void cs_detach(struct pcmcia_device *); static struct pcmcia_device *cur_dev = NULL; static const dev_info_t dev_info = "ni_mio_cs"; -static dev_node_t dev_node = { - "ni_mio_cs", - COMEDI_MAJOR, 0, - NULL -}; static int cs_attach(struct pcmcia_device *link) { link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; link->io.NumPorts1 = 16; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -292,8 +286,7 @@ static void cs_detach(struct pcmcia_device *link) { DPRINTK("cs_detach(link=%p)\n", link); - if (link->dev_node) - cs_release(link); + cs_release(link); } static int mio_cs_suspend(struct pcmcia_device *link) @@ -344,14 +337,10 @@ static void mio_cs_config(struct pcmcia_device *link) return; } - ret = pcmcia_request_irq(link, &link->irq); - if (ret) { - printk("pcmcia_request_irq() returned error: %i\n", ret); - } + if (!link->irq) + dev_info(&link->dev, "no IRQ available\n"); ret = pcmcia_request_configuration(link, &link->conf); - - link->dev_node = &dev_node; } static int mio_cs_attach(struct comedi_device *dev, struct comedi_devconfig *it) @@ -369,7 +358,7 @@ static int mio_cs_attach(struct comedi_device *dev, struct comedi_devconfig *it) dev->driver = &driver_ni_mio_cs; dev->iobase = link->io.BasePort1; - irq = link->irq.AssignedIRQ; + irq = link->irq; printk("comedi%d: %s: DAQCard: io 0x%04lx, irq %u, ", dev->minor, dev->driver->driver_name, dev->iobase, irq); diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c index 3325f24448b5..1786db2f3378 100644 --- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c @@ -60,7 +60,6 @@ Devices: [Quatech] DAQP-208 (daqp), DAQP-308 struct local_info_t { struct pcmcia_device *link; - dev_node_t node; int stop; int table_index; char board_name[32]; @@ -1040,10 +1039,6 @@ static int daqp_cs_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = daqp_interrupt; - /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost @@ -1074,10 +1069,8 @@ static void daqp_cs_detach(struct pcmcia_device *link) dev_dbg(&link->dev, "daqp_cs_detach\n"); - if (link->dev_node) { - dev->stop = 1; - daqp_cs_release(link); - } + dev->stop = 1; + daqp_cs_release(link); /* Unlink device structure, and free it */ dev_table[dev->table_index] = NULL; @@ -1105,8 +1098,7 @@ static int daqp_pcmcia_config_loop(struct pcmcia_device *p_dev, return -ENODEV; /* Do we need to allocate an interrupt? */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -1133,7 +1125,6 @@ static int daqp_pcmcia_config_loop(struct pcmcia_device *p_dev, static void daqp_cs_config(struct pcmcia_device *link) { - struct local_info_t *dev = link->priv; int ret; dev_dbg(&link->dev, "daqp_cs_config\n"); @@ -1144,16 +1135,9 @@ static void daqp_cs_config(struct pcmcia_device *link) goto failed; } - /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. - */ - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } + ret = pcmcia_request_irq(link, daqp_interrupt); + if (ret) + goto failed; /* This actually configures the PCMCIA socket -- setting up @@ -1164,23 +1148,10 @@ static void daqp_cs_config(struct pcmcia_device *link) if (ret) goto failed; - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - /* Comedi's PCMCIA script uses this device name (extracted - * from /var/lib/pcmcia/stab) to pass to comedi_config - */ - /* sprintf(dev->node.dev_name, "daqp%d", dev->table_index); */ - sprintf(dev->node.dev_name, "quatech_daqp_cs"); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x", link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %u", link->irq.AssignedIRQ); + printk(", irq %u", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1 + link->io.NumPorts1 - 1); diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/staging/cx25821/cx25821-alsa.c index 061add30ba8a..1798975a69bd 100644 --- a/drivers/staging/cx25821/cx25821-alsa.c +++ b/drivers/staging/cx25821/cx25821-alsa.c @@ -29,7 +29,7 @@ #include <linux/pci.h> #include <linux/slab.h> -#include <asm/delay.h> +#include <linux/delay.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -42,10 +42,10 @@ #define AUDIO_SRAM_CHANNEL SRAM_CH08 -#define dprintk(level,fmt, arg...) if (debug >= level) \ +#define dprintk(level, fmt, arg...) if (debug >= level) \ printk(KERN_INFO "%s/1: " fmt, chip->dev->name , ## arg) -#define dprintk_core(level,fmt, arg...) if (debug >= level) \ +#define dprintk_core(level, fmt, arg...) if (debug >= level) \ printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name , ## arg) /**************************************************************************** @@ -81,7 +81,6 @@ struct cx25821_audio_dev { struct snd_pcm_substream *substream; }; -typedef struct cx25821_audio_dev snd_cx25821_card_t; /**************************************************************************** @@ -90,7 +89,7 @@ typedef struct cx25821_audio_dev snd_cx25821_card_t; static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static int enable[SNDRV_CARDS] = { 1,[1 ... (SNDRV_CARDS - 1)] = 1 }; +static int enable[SNDRV_CARDS] = { 1, [1 ... (SNDRV_CARDS - 1)] = 1 }; module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled."); @@ -105,7 +104,7 @@ MODULE_PARM_DESC(index, "Index value for cx25821 capture interface(s)."); MODULE_DESCRIPTION("ALSA driver module for cx25821 based capture cards"); MODULE_AUTHOR("Hiep Huynh"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Conexant,25821}"); //"{{Conexant,23881}," +MODULE_SUPPORTED_DEVICE("{{Conexant,25821}"); /* "{{Conexant,23881}," */ static unsigned int debug; module_param(debug, int, 0644); @@ -135,7 +134,7 @@ MODULE_PARM_DESC(debug, "enable debug messages"); * BOARD Specific: Sets audio DMA */ -static int _cx25821_start_audio_dma(snd_cx25821_card_t * chip) +static int _cx25821_start_audio_dma(struct cx25821_audio_dev *chip) { struct cx25821_buffer *buf = chip->buf; struct cx25821_dev *dev = chip->dev; @@ -143,7 +142,7 @@ static int _cx25821_start_audio_dma(snd_cx25821_card_t * chip) &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]; u32 tmp = 0; - // enable output on the GPIO 0 for the MCLK ADC (Audio) + /* enable output on the GPIO 0 for the MCLK ADC (Audio) */ cx25821_set_gpiopin_direction(chip->dev, 0, 0); /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ @@ -158,18 +157,23 @@ static int _cx25821_start_audio_dma(snd_cx25821_card_t * chip) cx_write(AUD_A_LNGTH, buf->bpl); /* reset counter */ - cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); //GP_COUNT_CONTROL_RESET = 0x3 + /* GP_COUNT_CONTROL_RESET = 0x3 */ + cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); atomic_set(&chip->count, 0); - //Set the input mode to 16-bit + /* Set the input mode to 16-bit */ tmp = cx_read(AUD_A_CFG); cx_write(AUD_A_CFG, tmp | FLD_AUD_DST_PK_MODE | FLD_AUD_DST_ENABLE | FLD_AUD_CLK_ENABLE); - //printk(KERN_INFO "DEBUG: Start audio DMA, %d B/line, cmds_start(0x%x)= %d lines/FIFO, %d periods, %d " - // "byte buffer\n", buf->bpl, audio_ch->cmds_start, cx_read(audio_ch->cmds_start + 12)>>1, - // chip->num_periods, buf->bpl * chip->num_periods); + /* printk(KERN_INFO "DEBUG: Start audio DMA, %d B/line," + "cmds_start(0x%x)= %d lines/FIFO, %d periods, " + "%d byte buffer\n", buf->bpl, + audio_ch->cmds_start, + cx_read(audio_ch->cmds_start + 12)>>1, + chip->num_periods, buf->bpl *chip->num_periods); + */ /* Enables corresponding bits at AUD_INT_STAT */ cx_write(AUD_A_INT_MSK, @@ -182,7 +186,7 @@ static int _cx25821_start_audio_dma(snd_cx25821_card_t * chip) /* enable audio irqs */ cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT); - // Turn on audio downstream fifo and risc enable 0x101 + /* Turn on audio downstream fifo and risc enable 0x101 */ tmp = cx_read(AUD_INT_DMA_CTL); cx_set(AUD_INT_DMA_CTL, tmp | (FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN)); @@ -194,7 +198,7 @@ static int _cx25821_start_audio_dma(snd_cx25821_card_t * chip) /* * BOARD Specific: Resets audio DMA */ -static int _cx25821_stop_audio_dma(snd_cx25821_card_t * chip) +static int _cx25821_stop_audio_dma(struct cx25821_audio_dev *chip) { struct cx25821_dev *dev = chip->dev; @@ -232,13 +236,13 @@ static char *cx25821_aud_irqs[32] = { /* * BOARD Specific: Threats IRQ audio specific calls */ -static void cx25821_aud_irq(snd_cx25821_card_t * chip, u32 status, u32 mask) +static void cx25821_aud_irq(struct cx25821_audio_dev *chip, u32 status, + u32 mask) { struct cx25821_dev *dev = chip->dev; - if (0 == (status & mask)) { + if (0 == (status & mask)) return; - } cx_write(AUD_A_INT_STAT, status); if (debug > 1 || (status & mask & ~0xff)) @@ -277,7 +281,7 @@ static void cx25821_aud_irq(snd_cx25821_card_t * chip, u32 status, u32 mask) */ static irqreturn_t cx25821_irq(int irq, void *dev_id) { - snd_cx25821_card_t *chip = dev_id; + struct cx25821_audio_dev *chip = dev_id; struct cx25821_dev *dev = chip->dev; u32 status, pci_status; u32 audint_status, audint_mask; @@ -318,11 +322,11 @@ static irqreturn_t cx25821_irq(int irq, void *dev_id) if (handled) cx_write(PCI_INT_STAT, pci_status); - out: +out: return IRQ_RETVAL(handled); } -static int dsp_buffer_free(snd_cx25821_card_t * chip) +static int dsp_buffer_free(struct cx25821_audio_dev *chip) { BUG_ON(!chip->dma_size); @@ -363,7 +367,8 @@ static struct snd_pcm_hardware snd_cx25821_digital_hw = { .period_bytes_max = DEFAULT_FIFO_SIZE / 3, .periods_min = 1, .periods_max = AUDIO_LINE_SIZE, - .buffer_bytes_max = (AUDIO_LINE_SIZE * AUDIO_LINE_SIZE), //128*128 = 16384 = 1024 * 16 + /* 128 * 128 = 16384 = 1024 * 16 */ + .buffer_bytes_max = (AUDIO_LINE_SIZE * AUDIO_LINE_SIZE), }; /* @@ -371,7 +376,7 @@ static struct snd_pcm_hardware snd_cx25821_digital_hw = { */ static int snd_cx25821_pcm_open(struct snd_pcm_substream *substream) { - snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; unsigned int bpl = 0; @@ -393,18 +398,19 @@ static int snd_cx25821_pcm_open(struct snd_pcm_substream *substream) if (cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != DEFAULT_FIFO_SIZE) { - bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3; //since there are 3 audio Clusters + /* since there are 3 audio Clusters */ + bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3; bpl &= ~7; /* must be multiple of 8 */ - if (bpl > AUDIO_LINE_SIZE) { + if (bpl > AUDIO_LINE_SIZE) bpl = AUDIO_LINE_SIZE; - } + runtime->hw.period_bytes_min = bpl; runtime->hw.period_bytes_max = bpl; } return 0; - _error: +_error: dprintk(1, "Error opening PCM!\n"); return err; } @@ -423,7 +429,7 @@ static int snd_cx25821_close(struct snd_pcm_substream *substream) static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); struct videobuf_dmabuf *dma; struct cx25821_buffer *buf; @@ -445,9 +451,8 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, if (NULL == buf) return -ENOMEM; - if (chip->period_size > AUDIO_LINE_SIZE) { + if (chip->period_size > AUDIO_LINE_SIZE) chip->period_size = AUDIO_LINE_SIZE; - } buf->vb.memory = V4L2_MEMORY_MMAP; buf->vb.field = V4L2_FIELD_NONE; @@ -474,7 +479,7 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, buf->vb.width, buf->vb.height, 1); if (ret < 0) { printk(KERN_INFO - "DEBUG: ERROR after cx25821_risc_databuffer_audio() \n"); + "DEBUG: ERROR after cx25821_risc_databuffer_audio()\n"); goto error; } @@ -494,7 +499,7 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, return 0; - error: +error: kfree(buf); return ret; } @@ -504,7 +509,7 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, */ static int snd_cx25821_hw_free(struct snd_pcm_substream *substream) { - snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); if (substream->runtime->dma_area) { dsp_buffer_free(chip); @@ -528,7 +533,7 @@ static int snd_cx25821_prepare(struct snd_pcm_substream *substream) static int snd_cx25821_card_trigger(struct snd_pcm_substream *substream, int cmd) { - snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); int err = 0; /* Local interrupts are already disabled by ALSA */ @@ -557,7 +562,7 @@ static int snd_cx25821_card_trigger(struct snd_pcm_substream *substream, static snd_pcm_uframes_t snd_cx25821_pointer(struct snd_pcm_substream *substream) { - snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; u16 count; @@ -593,10 +598,11 @@ static struct snd_pcm_ops snd_cx25821_pcm_ops = { }; /* - * ALSA create a PCM device: Called when initializing the board. Sets up the name and hooks up - * the callbacks + * ALSA create a PCM device: Called when initializing the board. + * Sets up the name and hooks up the callbacks */ -static int snd_cx25821_pcm(snd_cx25821_card_t * chip, int device, char *name) +static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device, + char *name) { struct snd_pcm *pcm; int err; @@ -636,7 +642,7 @@ MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl); * from the file. */ /* -static int snd_cx25821_free(snd_cx25821_card_t *chip) +static int snd_cx25821_free(struct cx25821_audio_dev *chip) { if (chip->irq >= 0) free_irq(chip->irq, chip); @@ -653,9 +659,9 @@ static int snd_cx25821_free(snd_cx25821_card_t *chip) */ static void snd_cx25821_dev_free(struct snd_card *card) { - snd_cx25821_card_t *chip = card->private_data; + struct cx25821_audio_dev *chip = card->private_data; - //snd_cx25821_free(chip); + /* snd_cx25821_free(chip); */ snd_card_free(chip->card); } @@ -665,23 +671,23 @@ static void snd_cx25821_dev_free(struct snd_card *card) static int cx25821_audio_initdev(struct cx25821_dev *dev) { struct snd_card *card; - snd_cx25821_card_t *chip; + struct cx25821_audio_dev *chip; int err; if (devno >= SNDRV_CARDS) { printk(KERN_INFO "DEBUG ERROR: devno >= SNDRV_CARDS %s\n", __func__); - return (-ENODEV); + return -ENODEV; } if (!enable[devno]) { ++devno; printk(KERN_INFO "DEBUG ERROR: !enable[devno] %s\n", __func__); - return (-ENOENT); + return -ENOENT; } err = snd_card_create(index[devno], id[devno], THIS_MODULE, - sizeof(snd_cx25821_card_t), &card); + sizeof(struct cx25821_audio_dev), &card); if (err < 0) { printk(KERN_INFO "DEBUG ERROR: cannot create snd_card_new in %s\n", @@ -693,7 +699,7 @@ static int cx25821_audio_initdev(struct cx25821_dev *dev) /* Card "creation" */ card->private_free = snd_cx25821_dev_free; - chip = (snd_cx25821_card_t *) card->private_data; + chip = (struct cx25821_audio_dev *) card->private_data; spin_lock_init(&chip->reg_lock); chip->dev = dev; @@ -712,7 +718,8 @@ static int cx25821_audio_initdev(struct cx25821_dev *dev) goto error; } - if ((err = snd_cx25821_pcm(chip, 0, "cx25821 Digital")) < 0) { + err = snd_cx25821_pcm(chip, 0, "cx25821 Digital"); + if (err < 0) { printk(KERN_INFO "DEBUG ERROR: cannot create snd_cx25821_pcm %s\n", __func__); @@ -741,7 +748,7 @@ static int cx25821_audio_initdev(struct cx25821_dev *dev) devno++; return 0; - error: +error: snd_card_free(card); return err; } diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.c b/drivers/staging/cx25821/cx25821-audio-upstream.c index 11c56bdb0ceb..6a4e8720b478 100644 --- a/drivers/staging/cx25821/cx25821-audio-upstream.c +++ b/drivers/staging/cx25821/cx25821-audio-upstream.c @@ -33,7 +33,7 @@ #include <linux/fcntl.h> #include <linux/delay.h> #include <linux/slab.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); @@ -62,9 +62,8 @@ int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, cdt = ch->cdt; lines = ch->fifo_size / bpl; - if (lines > 3) { + if (lines > 3) lines = 3; - } BUG_ON(lines < 2); @@ -84,7 +83,7 @@ int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW); cx_write(ch->cmds_start + 16, ch->ctrl_start); - //IQ size + /* IQ size */ cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW); for (i = 24; i < 80; i += 4) @@ -100,7 +99,7 @@ int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, } static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, - __le32 * rp, + __le32 *rp, dma_addr_t databuf_phys_addr, unsigned int bpl, int fifo_enable) @@ -116,8 +115,10 @@ static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, *(rp++) = cpu_to_le32(databuf_phys_addr + offset); *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - // Check if we need to enable the FIFO after the first 3 lines - // For the upstream audio channel, the risc engine will enable the FIFO. + /* Check if we need to enable the FIFO + * after the first 3 lines. + * For the upstream audio channel, + * the risc engine will enable the FIFO */ if (fifo_enable && line == 2) { *(rp++) = RISC_WRITECR; *(rp++) = sram_ch->dma_ctl; @@ -160,7 +161,7 @@ int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev, risc_flag = RISC_CNT_INC; } - //Calculate physical jump address + /* Calculate physical jump address */ if ((frame + 1) == NUM_AUDIO_FRAMES) { risc_phys_jump_addr = dev->_risc_phys_start_addr + @@ -179,17 +180,17 @@ int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev, fifo_enable); if (USE_RISC_NOOP_AUDIO) { - for (i = 0; i < NUM_NO_OPS; i++) { + for (i = 0; i < NUM_NO_OPS; i++) *(rp++) = cpu_to_le32(RISC_NOOP); - } } - // Loop to (Nth)FrameRISC or to Start of Risc program & generate IRQ + /* Loop to (Nth)FrameRISC or to Start of Risc program & + * generate IRQ */ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); *(rp++) = cpu_to_le32(risc_phys_jump_addr); *(rp++) = cpu_to_le32(0); - //Recalculate virtual address based on frame index + /* Recalculate virtual address based on frame index */ rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 + (AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4); } @@ -220,19 +221,19 @@ void cx25821_stop_upstream_audio(struct cx25821_dev *dev) u32 tmp = 0; if (!dev->_audio_is_running) { - printk - ("cx25821: No audio file is currently running so return!\n"); + printk(KERN_DEBUG + "cx25821: No audio file is currently running so return!\n"); return; } - //Disable RISC interrupts + /* Disable RISC interrupts */ cx_write(sram_ch->int_msk, 0); - //Turn OFF risc and fifo enable in AUD_DMA_CNTRL + /* Turn OFF risc and fifo enable in AUD_DMA_CNTRL */ tmp = cx_read(sram_ch->dma_ctl); cx_write(sram_ch->dma_ctl, tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en)); - //Clear data buffer memory + /* Clear data buffer memory */ if (dev->_audiodata_buf_virt_addr) memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size); @@ -253,9 +254,8 @@ void cx25821_stop_upstream_audio(struct cx25821_dev *dev) void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev) { - if (dev->_audio_is_running) { + if (dev->_audio_is_running) cx25821_stop_upstream_audio(dev); - } cx25821_free_memory_audio(dev); } @@ -282,7 +282,7 @@ int cx25821_get_audio_data(struct cx25821_dev *dev, if (IS_ERR(myfile)) { const int open_errno = -PTR_ERR(myfile); - printk("%s(): ERROR opening file(%s) with errno = %d! \n", + printk(KERN_ERR "%s(): ERROR opening file(%s) with errno = %d!\n", __func__, dev->_audiofilename, open_errno); return PTR_ERR(myfile); } else { @@ -294,7 +294,7 @@ int cx25821_get_audio_data(struct cx25821_dev *dev, } if (!myfile->f_op->read) { - printk("%s: File has no READ operations registered! \n", + printk("%s: File has no READ operations registered!\n", __func__); filp_close(myfile, NULL); return -EIO; @@ -347,7 +347,7 @@ static void cx25821_audioups_handler(struct work_struct *work) container_of(work, struct cx25821_dev, _audio_work_entry); if (!dev) { - printk("ERROR %s(): since container_of(work_struct) FAILED! \n", + printk(KERN_ERR "ERROR %s(): since container_of(work_struct) FAILED!\n", __func__); return; } @@ -373,19 +373,19 @@ int cx25821_openfile_audio(struct cx25821_dev *dev, if (IS_ERR(myfile)) { const int open_errno = -PTR_ERR(myfile); - printk("%s(): ERROR opening file(%s) with errno = %d! \n", + printk(KERN_ERR "%s(): ERROR opening file(%s) with errno = %d!\n", __func__, dev->_audiofilename, open_errno); return PTR_ERR(myfile); } else { if (!(myfile->f_op)) { - printk("%s: File has no file operations registered! \n", + printk("%s: File has no file operations registered!\n", __func__); filp_close(myfile, NULL); return -EIO; } if (!myfile->f_op->read) { - printk("%s: File has no READ operations registered! \n", + printk("%s: File has no READ operations registered!\n", __func__); filp_close(myfile, NULL); return -EIO; @@ -421,13 +421,11 @@ int cx25821_openfile_audio(struct cx25821_dev *dev, } } - if (i > 0) { + if (i > 0) dev->_audioframe_count++; - } - if (vfs_read_retval < line_size) { + if (vfs_read_retval < line_size) break; - } } dev->_audiofile_status = @@ -460,14 +458,14 @@ static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, dev->_audiorisc_size = dev->audio_upstream_riscbuf_size; if (!dev->_risc_virt_addr) { - printk - ("cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning.\n"); + printk(KERN_DEBUG + "cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning.\n"); return -ENOMEM; } - //Clear out memory at address + /* Clear out memory at address */ memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size); - //For Audio Data buffer allocation + /* For Audio Data buffer allocation */ dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci, dev->audio_upstream_databuf_size, &data_dma_addr); @@ -475,30 +473,30 @@ static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, dev->_audiodata_buf_size = dev->audio_upstream_databuf_size; if (!dev->_audiodata_buf_virt_addr) { - printk - ("cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning. \n"); + printk(KERN_DEBUG + "cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning.\n"); return -ENOMEM; } - //Clear out memory at address + /* Clear out memory at address */ memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size); ret = cx25821_openfile_audio(dev, sram_ch); if (ret < 0) return ret; - //Creating RISC programs + /* Creating RISC programs */ ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, dev->_audio_lines_count); if (ret < 0) { printk(KERN_DEBUG - "cx25821 ERROR creating audio upstream RISC programs! \n"); + "cx25821 ERROR creating audio upstream RISC programs!\n"); goto error; } return 0; - error: +error: return ret; } @@ -512,22 +510,22 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, __le32 *rp; if (status & FLD_AUD_SRC_RISCI1) { - //Get interrupt_index of the program that interrupted + /* Get interrupt_index of the program that interrupted */ u32 prog_cnt = cx_read(channel->gpcnt); - //Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers + /* Since we've identified our IRQ, clear our bits from the + * interrupt mask and interrupt status registers */ cx_write(channel->int_msk, 0); cx_write(channel->int_stat, cx_read(channel->int_stat)); spin_lock(&dev->slock); while (prog_cnt != dev->_last_index_irq) { - //Update _last_index_irq - if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1)) { + /* Update _last_index_irq */ + if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1)) dev->_last_index_irq++; - } else { + else dev->_last_index_irq = 0; - } dev->_audioframe_index = dev->_last_index_irq; @@ -559,7 +557,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, cpu_to_le32(RISC_NOOP); } } - // Jump to 2nd Audio Frame + /* Jump to 2nd Audio Frame */ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_RESET); @@ -582,7 +580,8 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, printk("%s: Audio Received OpCode Error Interrupt!\n", __func__); - // Read and write back the interrupt status register to clear our bits + /* Read and write back the interrupt status register to clear + * our bits */ cx_write(channel->int_stat, cx_read(channel->int_stat)); } @@ -591,7 +590,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, dev->_audioframe_count); return -1; } - //ElSE, set the interrupt mask register, re-enable irq. + /* ElSE, set the interrupt mask register, re-enable irq. */ int_msk_tmp = cx_read(channel->int_msk); cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); @@ -613,7 +612,7 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) msk_stat = cx_read(sram_ch->int_mstat); audio_status = cx_read(sram_ch->int_stat); - // Only deal with our interrupt + /* Only deal with our interrupt */ if (audio_status) { handled = cx25821_audio_upstream_irq(dev, @@ -622,11 +621,10 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) audio_status); } - if (handled < 0) { + if (handled < 0) cx25821_stop_upstream_audio(dev); - } else { + else handled += handled; - } return IRQ_RETVAL(handled); } @@ -638,13 +636,14 @@ static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, u32 tmp; do { - //Wait 10 microsecond before checking to see if the FIFO is turned ON. + /* Wait 10 microsecond before checking to see if the FIFO is + * turned ON. */ udelay(10); tmp = cx_read(sram_ch->dma_ctl); - if (count++ > 1000) //10 millisecond timeout - { + /* 10 millisecond timeout */ + if (count++ > 1000) { printk ("cx25821 ERROR: %s() fifo is NOT turned on. Timeout!\n", __func__); @@ -661,31 +660,34 @@ int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, u32 tmp = 0; int err = 0; - // Set the physical start address of the RISC program in the initial program counter(IPC) member of the CMDS. + /* Set the physical start address of the RISC program in the initial + * program counter(IPC) member of the CMDS. */ cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr); - cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ + /* Risc IPC High 64 bits 63-32 */ + cx_write(sram_ch->cmds_start + 4, 0); /* reset counter */ cx_write(sram_ch->gpcnt_ctl, 3); - //Set the line length (It looks like we do not need to set the line length) + /* Set the line length (It looks like we do not need to set the + * line length) */ cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH); - //Set the input mode to 16-bit + /* Set the input mode to 16-bit */ tmp = cx_read(sram_ch->aud_cfg); tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | FLD_AUD_SONY_MODE; cx_write(sram_ch->aud_cfg, tmp); - // Read and write back the interrupt status register to clear it + /* Read and write back the interrupt status register to clear it */ tmp = cx_read(sram_ch->int_stat); cx_write(sram_ch->int_stat, tmp); - // Clear our bits from the interrupt status register. + /* Clear our bits from the interrupt status register. */ cx_write(sram_ch->int_stat, _intr_msk); - //Set the interrupt mask register, enable irq. + /* Set the interrupt mask register, enable irq. */ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); tmp = cx_read(sram_ch->int_msk); cx_write(sram_ch->int_msk, tmp |= _intr_msk); @@ -699,19 +701,19 @@ int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, goto fail_irq; } - // Start the DMA engine + /* Start the DMA engine */ tmp = cx_read(sram_ch->dma_ctl); cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en); dev->_audio_is_running = 1; dev->_is_first_audio_frame = 1; - // The fifo_en bit turns on by the first Risc program + /* The fifo_en bit turns on by the first Risc program */ cx25821_wait_fifo_enable(dev, sram_ch); return 0; - fail_irq: +fail_irq: cx25821_dev_unregister(dev); return err; } @@ -731,14 +733,14 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) dev->_audio_upstream_channel_select = channel_select; sram_ch = &dev->sram_channels[channel_select]; - //Work queue + /* Work queue */ INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); dev->_irq_audio_queues = create_singlethread_workqueue("cx25821_audioworkqueue"); if (!dev->_irq_audio_queues) { - printk - ("cx25821 ERROR: create_singlethread_workqueue() for Audio FAILED!\n"); + printk(KERN_DEBUG + "cx25821 ERROR: create_singlethread_workqueue() for Audio FAILED!\n"); return -ENOMEM; } @@ -760,7 +762,7 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) memcpy(dev->_audiofilename, dev->input_audiofilename, str_length + 1); - //Default if filename is empty string + /* Default if filename is empty string */ if (strcmp(dev->input_audiofilename, "") == 0) { dev->_audiofilename = "/root/audioGOOD.wav"; } @@ -784,7 +786,7 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) RISC_SYNC_INSTRUCTION_SIZE; dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS; - //Allocating buffers and prepare RISC program + /* Allocating buffers and prepare RISC program */ retval = cx25821_audio_upstream_buffer_prepare(dev, sram_ch, _line_size); if (retval < 0) { @@ -793,12 +795,12 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) dev->name); goto error; } - //Start RISC engine + /* Start RISC engine */ cx25821_start_audio_dma_upstream(dev, sram_ch); return 0; - error: +error: cx25821_dev_unregister(dev); return err; diff --git a/drivers/staging/cx25821/cx25821-audups11.c b/drivers/staging/cx25821/cx25821-audups11.c index e76451c309f1..e49ead982f39 100644 --- a/drivers/staging/cx25821/cx25821-audups11.c +++ b/drivers/staging/cx25821/cx25821-audups11.c @@ -88,10 +88,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -145,7 +145,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO11)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO11)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -163,7 +163,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO11)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO11)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -191,19 +191,19 @@ static int video_release(struct file *file) //cx_write(channel11->dma_ctl, 0); /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO11)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO11)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO11); + cx25821_res_free(dev, fh, RESOURCE_VIDEO11); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -224,7 +224,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO11)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO11)))) { return -EBUSY; } @@ -242,11 +242,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO11); + res = cx25821_get_resource(fh, RESOURCE_VIDEO11); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -258,13 +258,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -350,7 +350,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, if (fh) { dev = fh->dev; - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -364,50 +364,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl_upstream11, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-cards.c b/drivers/staging/cx25821/cx25821-cards.c index 4d0b9eac3e49..da0f56d50e27 100644 --- a/drivers/staging/cx25821/cx25821-cards.c +++ b/drivers/staging/cx25821/cx25821-cards.c @@ -30,12 +30,12 @@ #include "cx25821.h" #include "tuner-xc2028.h" -// board config info +/* board config info */ struct cx25821_board cx25821_boards[] = { [UNKNOWN_BOARD] = { .name = "UNKNOWN/GENERIC", - // Ensure safe default for unknown boards + /* Ensure safe default for unknown boards */ .clk_freq = 0, }, diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/staging/cx25821/cx25821-core.c index 9e9b8c3c9311..d90abb383fc8 100644 --- a/drivers/staging/cx25821/cx25821-core.c +++ b/drivers/staging/cx25821/cx25821-core.c @@ -32,6 +32,7 @@ MODULE_AUTHOR("Shu Lin - Hiep Huynh"); MODULE_LICENSE("GPL"); struct list_head cx25821_devlist; +EXPORT_SYMBOL(cx25821_devlist); static unsigned int debug; module_param(debug, int, 0644); @@ -313,6 +314,7 @@ struct sram_channel cx25821_sram_channels[] = { .irq_bit = 11, }, }; +EXPORT_SYMBOL(cx25821_sram_channels); struct sram_channel *channel0 = &cx25821_sram_channels[SRAM_CH00]; struct sram_channel *channel1 = &cx25821_sram_channels[SRAM_CH01]; @@ -388,70 +390,74 @@ static void cx25821_registers_init(struct cx25821_dev *dev) { u32 tmp; - // enable RUN_RISC in Pecos + /* enable RUN_RISC in Pecos */ cx_write(DEV_CNTRL2, 0x20); - // Set the master PCI interrupt masks to enable video, audio, MBIF, and GPIO interrupts - // I2C interrupt masking is handled by the I2C objects themselves. + /* Set the master PCI interrupt masks to enable video, audio, MBIF, + * and GPIO interrupts + * I2C interrupt masking is handled by the I2C objects themselves. */ cx_write(PCI_INT_MSK, 0x2001FFFF); tmp = cx_read(RDR_TLCTL0); - tmp &= ~FLD_CFG_RCB_CK_EN; // Clear the RCB_CK_EN bit + tmp &= ~FLD_CFG_RCB_CK_EN; /* Clear the RCB_CK_EN bit */ cx_write(RDR_TLCTL0, tmp); - // PLL-A setting for the Audio Master Clock + /* PLL-A setting for the Audio Master Clock */ cx_write(PLL_A_INT_FRAC, 0x9807A58B); - // PLL_A_POST = 0x1C, PLL_A_OUT_TO_PIN = 0x1 + /* PLL_A_POST = 0x1C, PLL_A_OUT_TO_PIN = 0x1 */ cx_write(PLL_A_POST_STAT_BIST, 0x8000019C); - // clear reset bit [31] + /* clear reset bit [31] */ tmp = cx_read(PLL_A_INT_FRAC); cx_write(PLL_A_INT_FRAC, tmp & 0x7FFFFFFF); - // PLL-B setting for Mobilygen Host Bus Interface + /* PLL-B setting for Mobilygen Host Bus Interface */ cx_write(PLL_B_INT_FRAC, 0x9883A86F); - // PLL_B_POST = 0xD, PLL_B_OUT_TO_PIN = 0x0 + /* PLL_B_POST = 0xD, PLL_B_OUT_TO_PIN = 0x0 */ cx_write(PLL_B_POST_STAT_BIST, 0x8000018D); - // clear reset bit [31] + /* clear reset bit [31] */ tmp = cx_read(PLL_B_INT_FRAC); cx_write(PLL_B_INT_FRAC, tmp & 0x7FFFFFFF); - // PLL-C setting for video upstream channel + /* PLL-C setting for video upstream channel */ cx_write(PLL_C_INT_FRAC, 0x96A0EA3F); - // PLL_C_POST = 0x3, PLL_C_OUT_TO_PIN = 0x0 + /* PLL_C_POST = 0x3, PLL_C_OUT_TO_PIN = 0x0 */ cx_write(PLL_C_POST_STAT_BIST, 0x80000103); - // clear reset bit [31] + /* clear reset bit [31] */ tmp = cx_read(PLL_C_INT_FRAC); cx_write(PLL_C_INT_FRAC, tmp & 0x7FFFFFFF); - // PLL-D setting for audio upstream channel + /* PLL-D setting for audio upstream channel */ cx_write(PLL_D_INT_FRAC, 0x98757F5B); - // PLL_D_POST = 0x13, PLL_D_OUT_TO_PIN = 0x0 + /* PLL_D_POST = 0x13, PLL_D_OUT_TO_PIN = 0x0 */ cx_write(PLL_D_POST_STAT_BIST, 0x80000113); - // clear reset bit [31] + /* clear reset bit [31] */ tmp = cx_read(PLL_D_INT_FRAC); cx_write(PLL_D_INT_FRAC, tmp & 0x7FFFFFFF); - // This selects the PLL C clock source for the video upstream channel I and J + /* This selects the PLL C clock source for the video upstream channel + * I and J */ tmp = cx_read(VID_CH_CLK_SEL); cx_write(VID_CH_CLK_SEL, (tmp & 0x00FFFFFF) | 0x24000000); - // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C - //select 656/VIP DST for downstream Channel A - C + /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for + * channel A-C + * select 656/VIP DST for downstream Channel A - C */ tmp = cx_read(VID_CH_MODE_SEL); - //cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF); + /* cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF); */ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); - // enables 656 port I and J as output + /* enables 656 port I and J as output */ tmp = cx_read(CLK_RST); - tmp |= FLD_USE_ALT_PLL_REF; // use external ALT_PLL_REF pin as its reference clock instead + /* use external ALT_PLL_REF pin as its reference clock instead */ + tmp |= FLD_USE_ALT_PLL_REF; cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE)); mdelay(100); @@ -476,9 +482,8 @@ int cx25821_sram_channel_setup(struct cx25821_dev *dev, cdt = ch->cdt; lines = ch->fifo_size / bpl; - if (lines > 4) { + if (lines > 4) lines = 4; - } BUG_ON(lines < 2); @@ -494,16 +499,15 @@ int cx25821_sram_channel_setup(struct cx25821_dev *dev, cx_write(cdt + 16 * i + 12, 0); } - //init the first cdt buffer + /* init the first cdt buffer */ for (i = 0; i < 128; i++) cx_write(ch->fifo_start + 4 * i, i); /* write CMDS */ - if (ch->jumponly) { + if (ch->jumponly) cx_write(ch->cmds_start + 0, 8); - } else { + else cx_write(ch->cmds_start + 0, risc); - } cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ cx_write(ch->cmds_start + 8, cdt); @@ -526,6 +530,7 @@ int cx25821_sram_channel_setup(struct cx25821_dev *dev, return 0; } +EXPORT_SYMBOL(cx25821_sram_channel_setup); int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, struct sram_channel *ch, @@ -546,9 +551,8 @@ int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, cdt = ch->cdt; lines = ch->fifo_size / bpl; - if (lines > 3) { - lines = 3; //for AUDIO - } + if (lines > 3) + lines = 3; /* for AUDIO */ BUG_ON(lines < 2); @@ -565,25 +569,23 @@ int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, } /* write CMDS */ - if (ch->jumponly) { + if (ch->jumponly) cx_write(ch->cmds_start + 0, 8); - } else { + else cx_write(ch->cmds_start + 0, risc); - } cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ cx_write(ch->cmds_start + 8, cdt); cx_write(ch->cmds_start + 12, (lines * 16) >> 3); cx_write(ch->cmds_start + 16, ch->ctrl_start); - //IQ size - if (ch->jumponly) { + /* IQ size */ + if (ch->jumponly) cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2)); - } else { + else cx_write(ch->cmds_start + 20, 64 >> 2); - } - //zero out + /* zero out */ for (i = 24; i < 80; i += 4) cx_write(ch->cmds_start + i, 0); @@ -595,6 +597,7 @@ int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, return 0; } +EXPORT_SYMBOL(cx25821_sram_channel_setup_audio); void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch) { @@ -658,6 +661,7 @@ void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch) printk(KERN_WARNING " : cnt2_reg: 0x%08x\n", cx_read(ch->cnt2_reg)); } +EXPORT_SYMBOL(cx25821_sram_channel_dump); void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, struct sram_channel *ch) @@ -731,7 +735,7 @@ void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, printk(KERN_WARNING "instruction %d = 0x%x\n", i, risc); } - //read data from the first cdt buffer + /* read data from the first cdt buffer */ risc = cx_read(AUD_A_CDT); printk(KERN_WARNING "\nread cdt loc=0x%x\n", risc); for (i = 0; i < 8; i++) { @@ -741,31 +745,32 @@ void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, printk(KERN_WARNING "\n\n"); value = cx_read(CLK_RST); - CX25821_INFO(" CLK_RST = 0x%x \n\n", value); + CX25821_INFO(" CLK_RST = 0x%x\n\n", value); value = cx_read(PLL_A_POST_STAT_BIST); - CX25821_INFO(" PLL_A_POST_STAT_BIST = 0x%x \n\n", value); + CX25821_INFO(" PLL_A_POST_STAT_BIST = 0x%x\n\n", value); value = cx_read(PLL_A_INT_FRAC); - CX25821_INFO(" PLL_A_INT_FRAC = 0x%x \n\n", value); + CX25821_INFO(" PLL_A_INT_FRAC = 0x%x\n\n", value); value = cx_read(PLL_B_POST_STAT_BIST); - CX25821_INFO(" PLL_B_POST_STAT_BIST = 0x%x \n\n", value); + CX25821_INFO(" PLL_B_POST_STAT_BIST = 0x%x\n\n", value); value = cx_read(PLL_B_INT_FRAC); - CX25821_INFO(" PLL_B_INT_FRAC = 0x%x \n\n", value); + CX25821_INFO(" PLL_B_INT_FRAC = 0x%x\n\n", value); value = cx_read(PLL_C_POST_STAT_BIST); - CX25821_INFO(" PLL_C_POST_STAT_BIST = 0x%x \n\n", value); + CX25821_INFO(" PLL_C_POST_STAT_BIST = 0x%x\n\n", value); value = cx_read(PLL_C_INT_FRAC); - CX25821_INFO(" PLL_C_INT_FRAC = 0x%x \n\n", value); + CX25821_INFO(" PLL_C_INT_FRAC = 0x%x\n\n", value); value = cx_read(PLL_D_POST_STAT_BIST); - CX25821_INFO(" PLL_D_POST_STAT_BIST = 0x%x \n\n", value); + CX25821_INFO(" PLL_D_POST_STAT_BIST = 0x%x\n\n", value); value = cx_read(PLL_D_INT_FRAC); - CX25821_INFO(" PLL_D_INT_FRAC = 0x%x \n\n", value); + CX25821_INFO(" PLL_D_INT_FRAC = 0x%x\n\n", value); value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); - CX25821_INFO(" AFE_AB_DIAG_CTRL (0x10900090) = 0x%x \n\n", value); + CX25821_INFO(" AFE_AB_DIAG_CTRL (0x10900090) = 0x%x\n\n", value); } +EXPORT_SYMBOL(cx25821_sram_channel_dump_audio); static void cx25821_shutdown(struct cx25821_dev *dev) { @@ -835,8 +840,8 @@ static void cx25821_initialize(struct cx25821_dev *dev) cx_write(AUD_E_INT_STAT, 0xffffffff); cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); - cx_write(PAD_CTRL, 0x12); //for I2C - cx25821_registers_init(dev); //init Pecos registers + cx_write(PAD_CTRL, 0x12); /* for I2C */ + cx25821_registers_init(dev); /* init Pecos registers */ mdelay(100); for (i = 0; i < VID_CHANNEL_NUM; i++) { @@ -847,7 +852,7 @@ static void cx25821_initialize(struct cx25821_dev *dev) dev->use_cif_resolution[i] = FALSE; } - //Probably only affect Downstream + /* Probably only affect Downstream */ for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) { cx25821_set_vip_mode(dev, &dev->sram_channels[i]); @@ -859,7 +864,7 @@ static void cx25821_initialize(struct cx25821_dev *dev) cx25821_gpio_init(dev); } -static int get_resources(struct cx25821_dev *dev) +static int cx25821_get_resources(struct cx25821_dev *dev) { if (request_mem_region (pci_resource_start(dev->pci, 0), pci_resource_len(dev->pci, 0), @@ -944,12 +949,11 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->clk_freq = 28000000; dev->sram_channels = cx25821_sram_channels; - if (dev->nr > 1) { + if (dev->nr > 1) CX25821_INFO("dev->nr > 1!"); - } /* board config */ - dev->board = 1; //card[dev->nr]; + dev->board = 1; /* card[dev->nr]; */ dev->_max_num_decoders = MAX_DECODERS; dev->pci_bus = dev->pci->bus->number; @@ -967,7 +971,7 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->i2c_bus[0].i2c_period = (0x07 << 24); /* 1.95MHz */ - if (get_resources(dev) < 0) { + if (cx25821_get_resources(dev) < 0) { printk(KERN_ERR "%s No more PCIe resources for " "subsystem: %04x:%04x\n", dev->name, dev->pci->subsystem_vendor, @@ -1007,8 +1011,8 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) cx25821_initialize(dev); cx25821_i2c_register(&dev->i2c_bus[0]); -// cx25821_i2c_register(&dev->i2c_bus[1]); -// cx25821_i2c_register(&dev->i2c_bus[2]); +/* cx25821_i2c_register(&dev->i2c_bus[1]); + * cx25821_i2c_register(&dev->i2c_bus[2]); */ CX25821_INFO("i2c register! bus->i2c_rc = %d\n", dev->i2c_bus[0].i2c_rc); @@ -1026,7 +1030,7 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) { - //Since we don't have template8 for Audio Downstream + /* Since we don't have template8 for Audio Downstream */ if (cx25821_video_register(dev, i, video_template[i - 1]) < 0) { printk(KERN_ERR "%s() Failed to register analog video adapters for Upstream channel %d.\n", @@ -1034,7 +1038,7 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) } } - // register IOCTL device + /* register IOCTL device */ dev->ioctl_dev = cx25821_vdev_init(dev, dev->pci, video_template[VIDEO_IOCTL_CH], "video"); @@ -1112,6 +1116,7 @@ void cx25821_dev_unregister(struct cx25821_dev *dev) cx25821_i2c_unregister(&dev->i2c_bus[0]); cx25821_iounmap(dev); } +EXPORT_SYMBOL(cx25821_dev_unregister); static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, @@ -1122,9 +1127,8 @@ static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, unsigned int line, todo; /* sync instruction */ - if (sync_line != NO_SYNC_LINE) { + if (sync_line != NO_SYNC_LINE) *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); - } /* scan lines */ sg = sglist; @@ -1299,7 +1303,8 @@ int cx25821_risc_databuffer_audio(struct pci_dev *pci, instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; instructions += 1; - if ((rc = btcx_riscmem_alloc(pci, risc, instructions * 12)) < 0) + rc = btcx_riscmem_alloc(pci, risc, instructions * 12); + if (rc < 0) return rc; /* write risc instructions */ @@ -1312,6 +1317,7 @@ int cx25821_risc_databuffer_audio(struct pci_dev *pci, BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); return 0; } +EXPORT_SYMBOL(cx25821_risc_databuffer_audio); int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, u32 reg, u32 mask, u32 value) @@ -1375,7 +1381,7 @@ static irqreturn_t cx25821_irq(int irq, void *dev_id) } } - out: +out: return IRQ_RETVAL(handled); } @@ -1399,12 +1405,14 @@ void cx25821_print_irqbits(char *name, char *tag, char **strings, } printk("\n"); } +EXPORT_SYMBOL(cx25821_print_irqbits); struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci) { struct cx25821_dev *dev = pci_get_drvdata(pci); return dev; } +EXPORT_SYMBOL(cx25821_dev_get); static int __devinit cx25821_initdev(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) @@ -1430,7 +1438,7 @@ static int __devinit cx25821_initdev(struct pci_dev *pci_dev, goto fail_unregister_device; } - printk(KERN_INFO "cx25821 Athena pci enable ! \n"); + printk(KERN_INFO "cx25821 Athena pci enable !\n"); if (cx25821_dev_setup(dev) < 0) { err = -EINVAL; @@ -1464,14 +1472,14 @@ static int __devinit cx25821_initdev(struct pci_dev *pci_dev, return 0; - fail_irq: - printk(KERN_INFO "cx25821 cx25821_initdev() can't get IRQ ! \n"); +fail_irq: + printk(KERN_INFO "cx25821 cx25821_initdev() can't get IRQ !\n"); cx25821_dev_unregister(dev); - fail_unregister_device: +fail_unregister_device: v4l2_device_unregister(&dev->v4l2_dev); - fail_free: +fail_free: kfree(dev); return err; } @@ -1536,16 +1544,6 @@ static void __exit cx25821_fini(void) pci_unregister_driver(&cx25821_pci_driver); } -EXPORT_SYMBOL(cx25821_devlist); -EXPORT_SYMBOL(cx25821_sram_channels); -EXPORT_SYMBOL(cx25821_print_irqbits); -EXPORT_SYMBOL(cx25821_dev_get); -EXPORT_SYMBOL(cx25821_dev_unregister); -EXPORT_SYMBOL(cx25821_sram_channel_setup); -EXPORT_SYMBOL(cx25821_sram_channel_dump); -EXPORT_SYMBOL(cx25821_sram_channel_setup_audio); -EXPORT_SYMBOL(cx25821_sram_channel_dump_audio); -EXPORT_SYMBOL(cx25821_risc_databuffer_audio); EXPORT_SYMBOL(cx25821_set_gpiopin_direction); module_init(cx25821_init); diff --git a/drivers/staging/cx25821/cx25821-gpio.c b/drivers/staging/cx25821/cx25821-gpio.c index e8a37b47e437..2f154b3658a1 100644 --- a/drivers/staging/cx25821/cx25821-gpio.c +++ b/drivers/staging/cx25821/cx25821-gpio.c @@ -31,7 +31,7 @@ void cx25821_set_gpiopin_direction(struct cx25821_dev *dev, u32 gpio_register = 0; u32 value = 0; - // Check for valid pinNumber + /* Check for valid pinNumber */ if (pin_number >= 47) return; @@ -39,14 +39,14 @@ void cx25821_set_gpiopin_direction(struct cx25821_dev *dev, bit = pin_number - 31; gpio_oe_reg = GPIO_HI_OE; } - // Here we will make sure that the GPIOs 0 and 1 are output. keep the rest as is + /* Here we will make sure that the GPIOs 0 and 1 are output. keep the + * rest as is */ gpio_register = cx_read(gpio_oe_reg); - if (pin_logic_value == 1) { + if (pin_logic_value == 1) value = gpio_register | Set_GPIO_Bit(bit); - } else { + else value = gpio_register & Clear_GPIO_Bit(bit); - } cx_write(gpio_oe_reg, value); } @@ -58,11 +58,12 @@ static void cx25821_set_gpiopin_logicvalue(struct cx25821_dev *dev, u32 gpio_reg = GPIO_LO; u32 value = 0; - // Check for valid pinNumber + /* Check for valid pinNumber */ if (pin_number >= 47) return; - cx25821_set_gpiopin_direction(dev, pin_number, 0); // change to output direction + /* change to output direction */ + cx25821_set_gpiopin_direction(dev, pin_number, 0); if (pin_number > 31) { bit = pin_number - 31; @@ -71,25 +72,23 @@ static void cx25821_set_gpiopin_logicvalue(struct cx25821_dev *dev, value = cx_read(gpio_reg); - if (pin_logic_value == 0) { + if (pin_logic_value == 0) value &= Clear_GPIO_Bit(bit); - } else { + else value |= Set_GPIO_Bit(bit); - } cx_write(gpio_reg, value); } void cx25821_gpio_init(struct cx25821_dev *dev) { - if (dev == NULL) { + if (dev == NULL) return; - } switch (dev->board) { case CX25821_BOARD_CONEXANT_ATHENA10: default: - //set GPIO 5 to select the path for Medusa/Athena + /* set GPIO 5 to select the path for Medusa/Athena */ cx25821_set_gpiopin_logicvalue(dev, 5, 1); mdelay(20); break; diff --git a/drivers/staging/cx25821/cx25821-i2c.c b/drivers/staging/cx25821/cx25821-i2c.c index f4f2681d8f1c..08f45b52df6a 100644 --- a/drivers/staging/cx25821/cx25821-i2c.c +++ b/drivers/staging/cx25821/cx25821-i2c.c @@ -28,7 +28,7 @@ static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); -static unsigned int i2c_scan = 0; +static unsigned int i2c_scan; module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); @@ -159,9 +159,9 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, return msg->len; - eio: +eio: retval = -EIO; - err: +err: if (i2c_debug) printk(KERN_ERR " ERR: %d\n", retval); return retval; @@ -223,9 +223,9 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, } return msg->len; - eio: +eio: retval = -EIO; - err: +err: if (i2c_debug) printk(KERN_ERR " ERR: %d\n", retval); return retval; @@ -266,7 +266,7 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) } return num; - err: +err: return retval; } @@ -319,7 +319,7 @@ int cx25821_i2c_register(struct cx25821_i2c *bus) bus->i2c_client.adapter = &bus->i2c_adap; - //set up the I2c + /* set up the I2c */ bus->i2c_client.addr = (0x88 >> 1); return bus->i2c_rc; diff --git a/drivers/staging/cx25821/cx25821-medusa-video.c b/drivers/staging/cx25821/cx25821-medusa-video.c index d6016200d699..34616dc507f9 100644 --- a/drivers/staging/cx25821/cx25821-medusa-video.c +++ b/drivers/staging/cx25821/cx25821-medusa-video.c @@ -24,11 +24,12 @@ #include "cx25821-medusa-video.h" #include "cx25821-biffuncs.h" -///////////////////////////////////////////////////////////////////////////////////////// -//medusa_enable_bluefield_output() -// -// Enable the generation of blue filed output if no video -// +/* + * medusa_enable_bluefield_output() + * + * Enable the generation of blue filed output if no video + * + */ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, int enable) { @@ -73,15 +74,15 @@ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, } value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl, &tmp); - value &= 0xFFFFFF7F; // clear BLUE_FIELD_EN + value &= 0xFFFFFF7F; /* clear BLUE_FIELD_EN */ if (enable) - value |= 0x00000080; // set BLUE_FIELD_EN + value |= 0x00000080; /* set BLUE_FIELD_EN */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp); value &= 0xFFFFFF7F; if (enable) - value |= 0x00000080; // set BLUE_FIELD_EN + value |= 0x00000080; /* set BLUE_FIELD_EN */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); } @@ -95,17 +96,18 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) mutex_lock(&dev->lock); for (i = 0; i < MAX_DECODERS; i++) { - // set video format NTSC-M + /* set video format NTSC-M */ value = cx25821_i2c_read(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), &tmp); value &= 0xFFFFFFF0; - value |= 0x10001; // enable the fast locking mode bit[16] + /* enable the fast locking mode bit[16] */ + value |= 0x10001; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), value); - // resolution NTSC 720x480 + /* resolution NTSC 720x480 */ value = cx25821_i2c_read(&dev->i2c_bus[0], HORIZ_TIM_CTRL + (0x200 * i), &tmp); @@ -119,17 +121,17 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) cx25821_i2c_read(&dev->i2c_bus[0], VERT_TIM_CTRL + (0x200 * i), &tmp); value &= 0x00C00C00; - value |= 0x1C1E001A; // vblank_cnt + 2 to get camera ID + value |= 0x1C1E001A; /* vblank_cnt + 2 to get camera ID */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VERT_TIM_CTRL + (0x200 * i), value); - // chroma subcarrier step size + /* chroma subcarrier step size */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], SC_STEP_SIZE + (0x200 * i), 0x43E00000); - // enable VIP optional active + /* enable VIP optional active */ value = cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL_NS + (0x200 * i), &tmp); @@ -139,7 +141,7 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL_NS + (0x200 * i), value); - // enable VIP optional active (VIP_OPT_AL) for direct output. + /* enable VIP optional active (VIP_OPT_AL) for direct output. */ value = cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), &tmp); @@ -149,19 +151,21 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), value); - // clear VPRES_VERT_EN bit, fixes the chroma run away problem - // when the input switching rate < 16 fields - // + /* + * clear VPRES_VERT_EN bit, fixes the chroma run away problem + * when the input switching rate < 16 fields + */ value = cx25821_i2c_read(&dev->i2c_bus[0], MISC_TIM_CTRL + (0x200 * i), &tmp); - value = setBitAtPos(value, 14); // disable special play detection + /* disable special play detection */ + value = setBitAtPos(value, 14); value = clearBitAtPos(value, 15); ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MISC_TIM_CTRL + (0x200 * i), value); - // set vbi_gate_en to 0 + /* set vbi_gate_en to 0 */ value = cx25821_i2c_read(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), &tmp); @@ -170,12 +174,12 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), value); - // Enable the generation of blue field output if no video + /* Enable the generation of blue field output if no video */ medusa_enable_bluefield_output(dev, i, 1); } for (i = 0; i < MAX_ENCODERS; i++) { - // NTSC hclock + /* NTSC hclock */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_1 + (0x100 * i), &tmp); @@ -185,7 +189,7 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_1 + (0x100 * i), value); - // burst begin and burst end + /* burst begin and burst end */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_2 + (0x100 * i), &tmp); @@ -204,7 +208,7 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_3 + (0x100 * i), value); - // set NTSC vblank, no phase alternation, 7.5 IRE pedestal + /* set NTSC vblank, no phase alternation, 7.5 IRE pedestal */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4 + (0x100 * i), &tmp); @@ -227,17 +231,19 @@ static int medusa_initialize_ntsc(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_6 + (0x100 * i), 0x009A89C1); - // Subcarrier Increment + /* Subcarrier Increment */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_7 + (0x100 * i), 0x21F07C1F); } - //set picture resolutions - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); //0 - 720 - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); //0 - 480 + /* set picture resolutions */ + /* 0 - 720 */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); + /* 0 - 480 */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); - // set Bypass input format to NTSC 525 lines + /* set Bypass input format to NTSC 525 lines */ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); value |= 0x00080200; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); @@ -252,7 +258,7 @@ static int medusa_PALCombInit(struct cx25821_dev *dev, int dec) int ret_val = -1; u32 value = 0, tmp = 0; - // Setup for 2D threshold + /* Setup for 2D threshold */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_HFS_CFG + (0x200 * dec), 0x20002861); @@ -263,7 +269,7 @@ static int medusa_PALCombInit(struct cx25821_dev *dev, int dec) cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_LF_CFG + (0x200 * dec), 0x200A1023); - // Setup flat chroma and luma thresholds + /* Setup flat chroma and luma thresholds */ value = cx25821_i2c_read(&dev->i2c_bus[0], COMB_FLAT_THRESH_CTRL + (0x200 * dec), &tmp); @@ -272,12 +278,12 @@ static int medusa_PALCombInit(struct cx25821_dev *dev, int dec) cx25821_i2c_write(&dev->i2c_bus[0], COMB_FLAT_THRESH_CTRL + (0x200 * dec), value); - // set comb 2D blend + /* set comb 2D blend */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_BLEND + (0x200 * dec), 0x210F0F0F); - // COMB MISC CONTROL + /* COMB MISC CONTROL */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], COMB_MISC_CTRL + (0x200 * dec), 0x41120A7F); @@ -295,17 +301,18 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) mutex_lock(&dev->lock); for (i = 0; i < MAX_DECODERS; i++) { - // set video format PAL-BDGHI + /* set video format PAL-BDGHI */ value = cx25821_i2c_read(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), &tmp); value &= 0xFFFFFFF0; - value |= 0x10004; // enable the fast locking mode bit[16] + /* enable the fast locking mode bit[16] */ + value |= 0x10004; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), value); - // resolution PAL 720x576 + /* resolution PAL 720x576 */ value = cx25821_i2c_read(&dev->i2c_bus[0], HORIZ_TIM_CTRL + (0x200 * i), &tmp); @@ -315,22 +322,22 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], HORIZ_TIM_CTRL + (0x200 * i), value); - // vblank656_cnt=x26, vactive_cnt=240h, vblank_cnt=x24 + /* vblank656_cnt=x26, vactive_cnt=240h, vblank_cnt=x24 */ value = cx25821_i2c_read(&dev->i2c_bus[0], VERT_TIM_CTRL + (0x200 * i), &tmp); value &= 0x00C00C00; - value |= 0x28240026; // vblank_cnt + 2 to get camera ID + value |= 0x28240026; /* vblank_cnt + 2 to get camera ID */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VERT_TIM_CTRL + (0x200 * i), value); - // chroma subcarrier step size + /* chroma subcarrier step size */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], SC_STEP_SIZE + (0x200 * i), 0x5411E2D0); - // enable VIP optional active + /* enable VIP optional active */ value = cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL_NS + (0x200 * i), &tmp); @@ -340,7 +347,7 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL_NS + (0x200 * i), value); - // enable VIP optional active (VIP_OPT_AL) for direct output. + /* enable VIP optional active (VIP_OPT_AL) for direct output. */ value = cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), &tmp); @@ -350,18 +357,21 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), value); - // clear VPRES_VERT_EN bit, fixes the chroma run away problem - // when the input switching rate < 16 fields + /* + * clear VPRES_VERT_EN bit, fixes the chroma run away problem + * when the input switching rate < 16 fields + */ value = cx25821_i2c_read(&dev->i2c_bus[0], MISC_TIM_CTRL + (0x200 * i), &tmp); - value = setBitAtPos(value, 14); // disable special play detection + /* disable special play detection */ + value = setBitAtPos(value, 14); value = clearBitAtPos(value, 15); ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MISC_TIM_CTRL + (0x200 * i), value); - // set vbi_gate_en to 0 + /* set vbi_gate_en to 0 */ value = cx25821_i2c_read(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), &tmp); @@ -372,12 +382,12 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) medusa_PALCombInit(dev, i); - // Enable the generation of blue field output if no video + /* Enable the generation of blue field output if no video */ medusa_enable_bluefield_output(dev, i, 1); } for (i = 0; i < MAX_ENCODERS; i++) { - // PAL hclock + /* PAL hclock */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_1 + (0x100 * i), &tmp); @@ -387,7 +397,7 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_1 + (0x100 * i), value); - // burst begin and burst end + /* burst begin and burst end */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_2 + (0x100 * i), &tmp); @@ -397,7 +407,7 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_2 + (0x100 * i), value); - // hblank and vactive + /* hblank and vactive */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_3 + (0x100 * i), &tmp); @@ -407,7 +417,7 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_3 + (0x100 * i), value); - // set PAL vblank, phase alternation, 0 IRE pedestal + /* set PAL vblank, phase alternation, 0 IRE pedestal */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4 + (0x100 * i), &tmp); @@ -430,17 +440,19 @@ static int medusa_initialize_pal(struct cx25821_dev *dev) cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_6 + (0x100 * i), 0x00A493CF); - // Subcarrier Increment + /* Subcarrier Increment */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_7 + (0x100 * i), 0x2A098ACB); } - //set picture resolutions - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); //0 - 720 - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); //0 - 576 + /* set picture resolutions */ + /* 0 - 720 */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); + /* 0 - 576 */ + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); - // set Bypass input format to PAL 625 lines + /* set Bypass input format to PAL 625 lines */ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); value &= 0xFFF7FDFF; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); @@ -455,18 +467,17 @@ int medusa_set_videostandard(struct cx25821_dev *dev) int status = STATUS_SUCCESS; u32 value = 0, tmp = 0; - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) { + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) status = medusa_initialize_pal(dev); - } else { + else status = medusa_initialize_ntsc(dev); - } - // Enable DENC_A output + /* Enable DENC_A output */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4, &tmp); value = setBitAtPos(value, 4); status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4, value); - // Enable DENC_B output + /* Enable DENC_B output */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_B_REG_4, &tmp); value = setBitAtPos(value, 4); status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_B_REG_4, value); @@ -486,10 +497,10 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, mutex_lock(&dev->lock); - // validate the width - cannot be negative + /* validate the width - cannot be negative */ if (width > MAX_WIDTH) { printk - ("cx25821 %s() : width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH \n", + ("cx25821 %s() : width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH\n", __func__, width, MAX_WIDTH); width = MAX_WIDTH; } @@ -523,14 +534,14 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, vscale = 0x1E00; break; - default: //720 + default: /* 720 */ hscale = 0x0; vscale = 0x0; break; } for (; decoder < decoder_count; decoder++) { - // write scaling values for each decoder + /* write scaling values for each decoder */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL + (0x200 * decoder), hscale); @@ -552,7 +563,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, mutex_lock(&dev->lock); - // no support + /* no support */ if (decoder < VDEC_A && decoder > VDEC_H) { mutex_unlock(&dev->lock); return; @@ -577,11 +588,10 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, _display_field_cnt[decoder] = duration; - // update hardware + /* update hardware */ fld_cnt = cx25821_i2c_read(&dev->i2c_bus[0], disp_cnt_reg, &tmp); - if (!(decoder % 2)) // EVEN decoder - { + if (!(decoder % 2)) { /* EVEN decoder */ fld_cnt &= 0xFFFF0000; fld_cnt |= duration; } else { @@ -594,8 +604,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, mutex_unlock(&dev->lock); } -///////////////////////////////////////////////////////////////////////////////////////// -// Map to Medusa register setting +/* Map to Medusa register setting */ static int mapM(int srcMin, int srcMax, int srcVal, int dstMin, int dstMax, int *dstVal) { @@ -603,20 +612,21 @@ static int mapM(int srcMin, int denominator; int quotient; - if ((srcMin == srcMax) || (srcVal < srcMin) || (srcVal > srcMax)) { + if ((srcMin == srcMax) || (srcVal < srcMin) || (srcVal > srcMax)) return -1; - } - // This is the overall expression used: - // *dstVal = (srcVal - srcMin)*(dstMax - dstMin) / (srcMax - srcMin) + dstMin; - // but we need to account for rounding so below we use the modulus - // operator to find the remainder and increment if necessary. + /* + * This is the overall expression used: + * *dstVal = + * (srcVal - srcMin)*(dstMax - dstMin) / (srcMax - srcMin) + dstMin; + * but we need to account for rounding so below we use the modulus + * operator to find the remainder and increment if necessary. + */ numerator = (srcVal - srcMin) * (dstMax - dstMin); denominator = srcMax - srcMin; quotient = numerator / denominator; - if (2 * (numerator % denominator) >= denominator) { + if (2 * (numerator % denominator) >= denominator) quotient++; - } *dstVal = quotient + dstMin; @@ -636,7 +646,6 @@ static unsigned long convert_to_twos(long numeric, unsigned long bits_len) } } -///////////////////////////////////////////////////////////////////////////////////////// int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder) { int ret_val = 0; @@ -665,7 +674,6 @@ int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder) return ret_val; } -///////////////////////////////////////////////////////////////////////////////////////// int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder) { int ret_val = 0; @@ -695,7 +703,6 @@ int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder) return ret_val; } -///////////////////////////////////////////////////////////////////////////////////////// int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder) { int ret_val = 0; @@ -727,7 +734,6 @@ int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder) return ret_val; } -///////////////////////////////////////////////////////////////////////////////////////// int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder) { int ret_val = 0; @@ -768,98 +774,90 @@ int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder) return ret_val; } -///////////////////////////////////////////////////////////////////////////////////////// -// Program the display sequence and monitor output. -// +/* Program the display sequence and monitor output. */ + int medusa_video_init(struct cx25821_dev *dev) { - u32 value = 0, tmp = 0; - int ret_val = 0; - int i = 0; + u32 value, tmp = 0; + int ret_val; + int i; mutex_lock(&dev->lock); _num_decoders = dev->_max_num_decoders; - // disable Auto source selection on all video decoders + /* disable Auto source selection on all video decoders */ value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); value &= 0xFFFFF0FF; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); + if (ret_val < 0) + goto error; - if (ret_val < 0) { - mutex_unlock(&dev->lock); - return -EINVAL; - } - // Turn off Master source switch enable + /* Turn off Master source switch enable */ value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); value &= 0xFFFFFFDF; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); - if (ret_val < 0) - return -EINVAL; + goto error; mutex_unlock(&dev->lock); - for (i = 0; i < _num_decoders; i++) { + for (i = 0; i < _num_decoders; i++) medusa_set_decoderduration(dev, i, _display_field_cnt[i]); - } mutex_lock(&dev->lock); - // Select monitor as DENC A input, power up the DAC + /* Select monitor as DENC A input, power up the DAC */ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp); value &= 0xFF70FF70; - value |= 0x00090008; // set en_active + value |= 0x00090008; /* set en_active */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_AB_CTRL, value); + if (ret_val < 0) + goto error; - if (ret_val < 0) { - mutex_unlock(&dev->lock); - return -EINVAL; - } - // enable input is VIP/656 + /* enable input is VIP/656 */ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); - value |= 0x00040100; // enable VIP + value |= 0x00040100; /* enable VIP */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); - if (ret_val < 0) { - mutex_unlock(&dev->lock); - return -EINVAL; - } - // select AFE clock to output mode + if (ret_val < 0) + goto error; + + /* select AFE clock to output mode */ value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); value &= 0x83FFFFFF; - ret_val = - cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, - value | 0x10000000); + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, + value | 0x10000000); + if (ret_val < 0) + goto error; - if (ret_val < 0) { - mutex_unlock(&dev->lock); - return -EINVAL; - } - // Turn on all of the data out and control output pins. + /* Turn on all of the data out and control output pins. */ value = cx25821_i2c_read(&dev->i2c_bus[0], PIN_OE_CTRL, &tmp); value &= 0xFEF0FE00; if (_num_decoders == MAX_DECODERS) { - // Note: The octal board does not support control pins(bit16-19). - // These bits are ignored in the octal board. - value |= 0x010001F8; // disable VDEC A-C port, default to Mobilygen Interface + /* + * Note: The octal board does not support control pins(bit16-19) + * These bits are ignored in the octal board. + * + * disable VDEC A-C port, default to Mobilygen Interface + */ + value |= 0x010001F8; } else { - value |= 0x010F0108; // disable VDEC A-C port, default to Mobilygen Interface + /* disable VDEC A-C port, default to Mobilygen Interface */ + value |= 0x010F0108; } value |= 7; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], PIN_OE_CTRL, value); - if (ret_val < 0) { - mutex_unlock(&dev->lock); - return -EINVAL; - } + if (ret_val < 0) + goto error; mutex_unlock(&dev->lock); ret_val = medusa_set_videostandard(dev); + return ret_val; - if (ret_val < 0) - return -EINVAL; - - return 1; +error: + mutex_unlock(&dev->lock); + return ret_val; } diff --git a/drivers/staging/cx25821/cx25821-video-upstream.c b/drivers/staging/cx25821/cx25821-video-upstream.c index 6d48a1e26d1b..c842d8f3d692 100644 --- a/drivers/staging/cx25821/cx25821-video-upstream.c +++ b/drivers/staging/cx25821/cx25821-video-upstream.c @@ -32,7 +32,7 @@ #include <linux/file.h> #include <linux/fcntl.h> #include <linux/slab.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); @@ -60,9 +60,8 @@ int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, cdt = ch->cdt; lines = ch->fifo_size / bpl; - if (lines > 4) { + if (lines > 4) lines = 4; - } BUG_ON(lines < 2); @@ -97,7 +96,7 @@ int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, } static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev, - __le32 * rp, unsigned int offset, + __le32 *rp, unsigned int offset, unsigned int bpl, u32 sync_line, unsigned int lines, int fifo_enable, int field_type) @@ -108,9 +107,8 @@ static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev, *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); if (USE_RISC_NOOP_VIDEO) { - for (i = 0; i < NUM_NO_OPS; i++) { + for (i = 0; i < NUM_NO_OPS; i++) *(rp++) = cpu_to_le32(RISC_NOOP); - } } /* scan lines */ @@ -140,14 +138,12 @@ static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, int dist_betwn_starts = bpl * 2; /* sync instruction */ - if (sync_line != NO_SYNC_LINE) { + if (sync_line != NO_SYNC_LINE) *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); - } if (USE_RISC_NOOP_VIDEO) { - for (i = 0; i < NUM_NO_OPS; i++) { + for (i = 0; i < NUM_NO_OPS; i++) *(rp++) = cpu_to_le32(RISC_NOOP); - } } /* scan lines */ @@ -157,12 +153,13 @@ static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, *(rp++) = cpu_to_le32(0); /* bits 63-32 */ if ((lines <= NTSC_FIELD_HEIGHT) - || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) { - offset += dist_betwn_starts; //to skip the other field line - } + || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) + /* to skip the other field line */ + offset += dist_betwn_starts; - // check if we need to enable the FIFO after the first 4 lines - // For the upstream video channel, the risc engine will enable the FIFO. + /* check if we need to enable the FIFO after the first 4 lines + * For the upstream video channel, the risc engine will enable + * the FIFO. */ if (fifo_enable && line == 3) { *(rp++) = RISC_WRITECR; *(rp++) = sram_ch->dma_ctl; @@ -181,7 +178,8 @@ int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, { __le32 *rp; int fifo_enable = 0; - int singlefield_lines = lines >> 1; //get line count for single field + /* get line count for single field */ + int singlefield_lines = lines >> 1; int odd_num_lines = singlefield_lines; int frame = 0; int frame_size = 0; @@ -225,7 +223,7 @@ int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, fifo_enable = FIFO_DISABLE; - //Even Field + /* Even Field */ rp = cx25821_risc_field_upstream(dev, rp, dev->_data_buf_phys_addr + databuf_offset, bottom_offset, @@ -241,7 +239,9 @@ int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, risc_flag = RISC_CNT_INC; } - // Loop to 2ndFrameRISC or to Start of Risc program & generate IRQ + /* Loop to 2ndFrameRISC or to Start of Risc + * program & generate IRQ + */ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); *(rp++) = cpu_to_le32(risc_phys_jump_addr); *(rp++) = cpu_to_le32(0); @@ -258,18 +258,18 @@ void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev) if (!dev->_is_running) { printk - ("cx25821: No video file is currently running so return!\n"); + (KERN_INFO "cx25821: No video file is currently running so return!\n"); return; } - //Disable RISC interrupts + /* Disable RISC interrupts */ tmp = cx_read(sram_ch->int_msk); cx_write(sram_ch->int_msk, tmp & ~_intr_msk); - //Turn OFF risc and fifo enable + /* Turn OFF risc and fifo enable */ tmp = cx_read(sram_ch->dma_ctl); cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); - //Clear data buffer memory + /* Clear data buffer memory */ if (dev->_data_buf_virt_addr) memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); @@ -292,9 +292,8 @@ void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev) void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev) { - if (dev->_is_running) { + if (dev->_is_running) cx25821_stop_upstream_video_ch1(dev); - } if (dev->_dma_virt_addr) { pci_free_consistent(dev->pci, dev->_risc_size, @@ -347,19 +346,19 @@ int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch) if (IS_ERR(myfile)) { const int open_errno = -PTR_ERR(myfile); - printk("%s(): ERROR opening file(%s) with errno = %d! \n", + printk(KERN_ERR "%s(): ERROR opening file(%s) with errno = %d!\n", __func__, dev->_filename, open_errno); return PTR_ERR(myfile); } else { if (!(myfile->f_op)) { - printk("%s: File has no file operations registered!", + printk(KERN_ERR "%s: File has no file operations registered!", __func__); filp_close(myfile, NULL); return -EIO; } if (!myfile->f_op->read) { - printk("%s: File has no READ operations registered!", + printk(KERN_ERR "%s: File has no READ operations registered!", __func__); filp_close(myfile, NULL); return -EIO; @@ -412,7 +411,7 @@ static void cx25821_vidups_handler(struct work_struct *work) container_of(work, struct cx25821_dev, _irq_work_entry); if (!dev) { - printk("ERROR %s(): since container_of(work_struct) FAILED! \n", + printk(KERN_ERR "ERROR %s(): since container_of(work_struct) FAILED!\n", __func__); return; } @@ -438,12 +437,12 @@ int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch) if (IS_ERR(myfile)) { const int open_errno = -PTR_ERR(myfile); - printk("%s(): ERROR opening file(%s) with errno = %d! \n", + printk(KERN_ERR "%s(): ERROR opening file(%s) with errno = %d!\n", __func__, dev->_filename, open_errno); return PTR_ERR(myfile); } else { if (!(myfile->f_op)) { - printk("%s: File has no file operations registered!", + printk(KERN_ERR "%s: File has no file operations registered!", __func__); filp_close(myfile, NULL); return -EIO; @@ -451,7 +450,7 @@ int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch) if (!myfile->f_op->read) { printk - ("%s: File has no READ operations registered! Returning.", + (KERN_ERR "%s: File has no READ operations registered! Returning.", __func__); filp_close(myfile, NULL); return -EIO; @@ -490,9 +489,8 @@ int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch) if (i > 0) dev->_frame_count++; - if (vfs_read_retval < line_size) { + if (vfs_read_retval < line_size) break; - } } dev->_file_status = @@ -528,11 +526,11 @@ int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, if (!dev->_dma_virt_addr) { printk - ("cx25821: FAILED to allocate memory for Risc buffer! Returning.\n"); + (KERN_ERR "cx25821: FAILED to allocate memory for Risc buffer! Returning.\n"); return -ENOMEM; } - //Clear memory at address + /* Clear memory at address */ memset(dev->_dma_virt_addr, 0, dev->_risc_size); if (dev->_data_buf_virt_addr != NULL) { @@ -540,7 +538,7 @@ int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, dev->_data_buf_virt_addr, dev->_data_buf_phys_addr); } - //For Video Data buffer allocation + /* For Video Data buffer allocation */ dev->_data_buf_virt_addr = pci_alloc_consistent(dev->pci, dev->upstream_databuf_size, &data_dma_addr); @@ -549,30 +547,30 @@ int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, if (!dev->_data_buf_virt_addr) { printk - ("cx25821: FAILED to allocate memory for data buffer! Returning.\n"); + (KERN_ERR "cx25821: FAILED to allocate memory for data buffer! Returning.\n"); return -ENOMEM; } - //Clear memory at address + /* Clear memory at address */ memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); ret = cx25821_openfile(dev, sram_ch); if (ret < 0) return ret; - //Create RISC programs + /* Create RISC programs */ ret = cx25821_risc_buffer_upstream(dev, dev->pci, 0, bpl, dev->_lines_count); if (ret < 0) { printk(KERN_INFO - "cx25821: Failed creating Video Upstream Risc programs! \n"); + "cx25821: Failed creating Video Upstream Risc programs!\n"); goto error; } return 0; - error: +error: return ret; } @@ -588,10 +586,11 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, __le32 *rp; if (status & FLD_VID_SRC_RISC1) { - // We should only process one program per call + /* We should only process one program per call */ u32 prog_cnt = cx_read(channel->gpcnt); - //Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers + /* Since we've identified our IRQ, clear our bits from the + * interrupt mask and interrupt status registers */ int_msk_tmp = cx_read(channel->int_msk); cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); cx_write(channel->int_stat, _intr_msk); @@ -632,7 +631,7 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, FIFO_DISABLE, ODD_FIELD); - // Jump to Even Risc program of 1st Frame + /* Jump to Even Risc program of 1st Frame */ *(rp++) = cpu_to_le32(RISC_JUMP); *(rp++) = cpu_to_le32(risc_phys_jump_addr); *(rp++) = cpu_to_le32(0); @@ -643,24 +642,24 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, } else { if (status & FLD_VID_SRC_UF) printk - ("%s: Video Received Underflow Error Interrupt!\n", + (KERN_ERR "%s: Video Received Underflow Error Interrupt!\n", __func__); if (status & FLD_VID_SRC_SYNC) - printk("%s: Video Received Sync Error Interrupt!\n", + printk(KERN_ERR "%s: Video Received Sync Error Interrupt!\n", __func__); if (status & FLD_VID_SRC_OPC_ERR) - printk("%s: Video Received OpCode Error Interrupt!\n", + printk(KERN_ERR "%s: Video Received OpCode Error Interrupt!\n", __func__); } if (dev->_file_status == END_OF_FILE) { - printk("cx25821: EOF Channel 1 Framecount = %d\n", + printk(KERN_ERR "cx25821: EOF Channel 1 Framecount = %d\n", dev->_frame_count); return -1; } - //ElSE, set the interrupt mask register, re-enable irq. + /* ElSE, set the interrupt mask register, re-enable irq. */ int_msk_tmp = cx_read(channel->int_msk); cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); @@ -685,17 +684,16 @@ static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); - // Only deal with our interrupt + /* Only deal with our interrupt */ if (vid_status) { handled = cx25821_video_upstream_irq(dev, channel_num, vid_status); } - if (handled < 0) { + if (handled < 0) cx25821_stop_upstream_video_ch1(dev); - } else { + else handled += handled; - } return IRQ_RETVAL(handled); } @@ -714,19 +712,19 @@ void cx25821_set_pixelengine(struct cx25821_dev *dev, struct sram_channel *ch, value |= dev->_isNTSC ? 0 : 0x10; cx_write(ch->vid_fmt_ctl, value); - // set number of active pixels in each line. Default is 720 pixels in both NTSC and PAL format + /* set number of active pixels in each line. + * Default is 720 pixels in both NTSC and PAL format */ cx_write(ch->vid_active_ctl1, width); num_lines = (height / 2) & 0x3FF; odd_num_lines = num_lines; - if (dev->_isNTSC) { + if (dev->_isNTSC) odd_num_lines += 1; - } value = (num_lines << 16) | odd_num_lines; - // set number of active lines in field 0 (top) and field 1 (bottom) + /* set number of active lines in field 0 (top) and field 1 (bottom) */ cx_write(ch->vid_active_ctl2, value); cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); @@ -738,21 +736,26 @@ int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, u32 tmp = 0; int err = 0; - // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for + * channel A-C + */ tmp = cx_read(VID_CH_MODE_SEL); cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - // Set the physical start address of the RISC program in the initial program counter(IPC) member of the cmds. + /* Set the physical start address of the RISC program in the initial + * program counter(IPC) member of the cmds. + */ cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr); - cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ + /* Risc IPC High 64 bits 63-32 */ + cx_write(sram_ch->cmds_start + 4, 0); /* reset counter */ cx_write(sram_ch->gpcnt_ctl, 3); - // Clear our bits from the interrupt status register. + /* Clear our bits from the interrupt status register. */ cx_write(sram_ch->int_stat, _intr_msk); - //Set the interrupt mask register, enable irq. + /* Set the interrupt mask register, enable irq. */ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); tmp = cx_read(sram_ch->int_msk); cx_write(sram_ch->int_msk, tmp |= _intr_msk); @@ -766,7 +769,7 @@ int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, goto fail_irq; } - // Start the DMA engine + /* Start the DMA engine */ tmp = cx_read(sram_ch->dma_ctl); cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); @@ -775,7 +778,7 @@ int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, return 0; - fail_irq: +fail_irq: cx25821_dev_unregister(dev); return err; } @@ -792,7 +795,7 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, int str_length = 0; if (dev->_is_running) { - printk("Video Channel is still running so return!\n"); + printk(KERN_INFO "Video Channel is still running so return!\n"); return 0; } @@ -804,10 +807,12 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, if (!dev->_irq_queues) { printk - ("cx25821: create_singlethread_workqueue() for Video FAILED!\n"); + (KERN_ERR "cx25821: create_singlethread_workqueue() for Video FAILED!\n"); return -ENOMEM; } - // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for + * channel A-C + */ tmp = cx_read(VID_CH_MODE_SEL); cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); @@ -841,7 +846,7 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, memcpy(dev->_filename, dev->_defaultname, str_length + 1); } - //Default if filename is empty string + /* Default if filename is empty string */ if (strcmp(dev->input_filename, "") == 0) { if (dev->_isNTSC) { dev->_filename = @@ -875,7 +880,7 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, dev->upstream_riscbuf_size = risc_buffer_size * 2; dev->upstream_databuf_size = data_frame_size * 2; - //Allocating buffers and prepare RISC program + /* Allocating buffers and prepare RISC program */ retval = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size); if (retval < 0) { printk(KERN_ERR @@ -888,7 +893,7 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, return 0; - error: +error: cx25821_dev_unregister(dev); return err; diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/staging/cx25821/cx25821-video.c index 8cd3986d2e5c..791212c1a661 100644 --- a/drivers/staging/cx25821/cx25821-video.c +++ b/drivers/staging/cx25821/cx25821-video.c @@ -54,34 +54,34 @@ static void init_controls(struct cx25821_dev *dev, int chan_num); struct cx25821_fmt formats[] = { { - .name = "8 bpp, gray", - .fourcc = V4L2_PIX_FMT_GREY, - .depth = 8, - .flags = FORMAT_FLAGS_PACKED, + .name = "8 bpp, gray", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8, + .flags = FORMAT_FLAGS_PACKED, }, { - .name = "4:1:1, packed, Y41P", - .fourcc = V4L2_PIX_FMT_Y41P, - .depth = 12, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "4:2:0, YUV", - .fourcc = V4L2_PIX_FMT_YUV420, - .depth = 12, - .flags = FORMAT_FLAGS_PACKED, - }, + .name = "4:1:1, packed, Y41P", + .fourcc = V4L2_PIX_FMT_Y41P, + .depth = 12, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:0, YUV", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .flags = FORMAT_FLAGS_PACKED, + }, }; -int get_format_size(void) +int cx25821_get_format_size(void) { return ARRAY_SIZE(formats); } @@ -102,7 +102,7 @@ struct cx25821_fmt *format_by_fourcc(unsigned int fourcc) return NULL; } -void dump_video_queue(struct cx25821_dev *dev, struct cx25821_dmaqueue *q) +void cx25821_dump_video_queue(struct cx25821_dev *dev, struct cx25821_dmaqueue *q) { struct cx25821_buffer *buf; struct list_head *item; @@ -212,7 +212,7 @@ static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) */ // resource management -int res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit) +int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit) { dprintk(1, "%s()\n", __func__); if (fh->resources & bit) @@ -234,17 +234,17 @@ int res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit) return 1; } -int res_check(struct cx25821_fh *fh, unsigned int bit) +int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit) { return fh->resources & bit; } -int res_locked(struct cx25821_dev *dev, unsigned int bit) +int cx25821_res_locked(struct cx25821_dev *dev, unsigned int bit) { return dev->resources & bit; } -void res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits) +void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits) { BUG_ON((fh->resources & bits) != bits); dprintk(1, "%s()\n", __func__); @@ -506,7 +506,7 @@ int cx25821_video_register(struct cx25821_dev *dev, int chan_num, return err; } -int buffer_setup(struct videobuf_queue *q, unsigned int *count, +int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { struct cx25821_fh *fh = q->priv_data; @@ -516,13 +516,13 @@ int buffer_setup(struct videobuf_queue *q, unsigned int *count, if (0 == *count) *count = 32; - while (*size * *count > vid_limit * 1024 * 1024) - (*count)--; + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; return 0; } -int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, +int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { struct cx25821_fh *fh = q->priv_data; @@ -648,7 +648,7 @@ int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, return rc; } -void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +void cx25821_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); @@ -667,7 +667,7 @@ struct videobuf_queue *get_queue(struct cx25821_fh *fh) } } -int get_resource(struct cx25821_fh *fh, int resource) +int cx25821_get_resource(struct cx25821_fh *fh, int resource) { switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -678,7 +678,7 @@ int get_resource(struct cx25821_fh *fh, int resource) } } -int video_mmap(struct file *file, struct vm_area_struct *vma) +int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) { struct cx25821_fh *fh = file->private_data; @@ -686,7 +686,7 @@ int video_mmap(struct file *file, struct vm_area_struct *vma) } /* VIDEO IOCTLS */ -int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx25821_fh *fh = priv; @@ -700,7 +700,7 @@ int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) return 0; } -int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx25821_fmt *fmt; enum v4l2_field field; @@ -746,7 +746,7 @@ int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) return 0; } -int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +int cx25821_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; @@ -761,7 +761,7 @@ int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) return 0; } -int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, +int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (unlikely(f->index >= ARRAY_SIZE(formats))) @@ -774,7 +774,7 @@ int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, } #ifdef CONFIG_VIDEO_V4L1_COMPAT -int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +int cx25821_vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { struct cx25821_fh *fh = priv; struct videobuf_queue *q; @@ -801,25 +801,25 @@ int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) } #endif -int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) +int cx25821_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { struct cx25821_fh *fh = priv; return videobuf_reqbufs(get_queue(fh), p); } -int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +int cx25821_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct cx25821_fh *fh = priv; return videobuf_querybuf(get_queue(fh), p); } -int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct cx25821_fh *fh = priv; return videobuf_qbuf(get_queue(fh), p); } -int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) +int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) { struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; @@ -828,7 +828,7 @@ int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) return 0; } -int vidioc_s_priority(struct file *file, void *f, enum v4l2_priority prio) +int cx25821_vidioc_s_priority(struct file *file, void *f, enum v4l2_priority prio) { struct cx25821_fh *fh = f; struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; @@ -837,7 +837,7 @@ int vidioc_s_priority(struct file *file, void *f, enum v4l2_priority prio) } #ifdef TUNER_FLAG -int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) +int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) { struct cx25821_fh *fh = priv; struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; @@ -846,7 +846,7 @@ int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) dprintk(1, "%s()\n", __func__); if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -891,14 +891,14 @@ int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i) return 0; } -int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) +int cx25821_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; dprintk(1, "%s()\n", __func__); return cx25821_enum_input(dev, i); } -int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; @@ -907,7 +907,7 @@ int vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -int vidioc_s_input(struct file *file, void *priv, unsigned int i) +int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct cx25821_fh *fh = priv; struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; @@ -916,7 +916,7 @@ int vidioc_s_input(struct file *file, void *priv, unsigned int i) dprintk(1, "%s(%d)\n", __func__, i); if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -933,7 +933,7 @@ int vidioc_s_input(struct file *file, void *priv, unsigned int i) } #ifdef TUNER_FLAG -int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) +int cx25821_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct cx25821_fh *fh = priv; struct cx25821_dev *dev = fh->dev; @@ -960,15 +960,14 @@ int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f) return 0; } -int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) +int cx25821_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct cx25821_fh *fh = priv; struct cx25821_dev *dev; int err; if (fh) { - dev = fh->dev; - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -978,7 +977,7 @@ int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) #endif #ifdef CONFIG_VIDEO_ADV_DEBUG -int vidioc_g_register(struct file *file, void *fh, +int cx25821_vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; @@ -991,7 +990,7 @@ int vidioc_g_register(struct file *file, void *fh, return 0; } -int vidioc_s_register(struct file *file, void *fh, +int cx25821_vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; @@ -1007,7 +1006,7 @@ int vidioc_s_register(struct file *file, void *fh, #endif #ifdef TUNER_FLAG -int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; @@ -1025,14 +1024,14 @@ int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) return 0; } -int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; struct cx25821_fh *fh = priv; int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -1108,7 +1107,7 @@ static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) return 0; } -int vidioc_queryctrl(struct file *file, void *priv, +int cx25821_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qctrl) { return cx25821_ctrl_query(qctrl); @@ -1127,7 +1126,7 @@ static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) return NULL; } -int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctl) +int cx25821_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctl) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; @@ -1216,7 +1215,7 @@ static void init_controls(struct cx25821_dev *dev, int chan_num) } } -int vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cropcap) +int cx25821_vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cropcap) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; @@ -1233,28 +1232,28 @@ int vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cropcap) return 0; } -int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; struct cx25821_fh *fh = priv; int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } - // vidioc_s_crop not supported + // cx25821_vidioc_s_crop not supported return -EINVAL; } -int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) { - // vidioc_g_crop not supported + // cx25821_vidioc_g_crop not supported return -EINVAL; } -int vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) +int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) { // medusa does not support video standard sensing of current input *norm = CX25821_NORMS; @@ -1262,7 +1261,7 @@ int vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) return 0; } -int is_valid_width(u32 width, v4l2_std_id tvnorm) +int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm) { if (tvnorm == V4L2_STD_PAL_BG) { if (width == 352 || width == 720) @@ -1280,7 +1279,7 @@ int is_valid_width(u32 width, v4l2_std_id tvnorm) return 0; } -int is_valid_height(u32 height, v4l2_std_id tvnorm) +int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm) { if (tvnorm == V4L2_STD_PAL_BG) { if (height == 576 || height == 288) diff --git a/drivers/staging/cx25821/cx25821-video.h b/drivers/staging/cx25821/cx25821-video.h index 4417ff5d90d4..0bddc02be57d 100644 --- a/drivers/staging/cx25821/cx25821-video.h +++ b/drivers/staging/cx25821/cx25821-video.h @@ -101,7 +101,7 @@ extern struct cx25821_fmt formats[]; extern struct cx25821_fmt *format_by_fourcc(unsigned int fourcc); extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; -extern void dump_video_queue(struct cx25821_dev *dev, +extern void cx25821_dump_video_queue(struct cx25821_dev *dev, struct cx25821_dmaqueue *q); extern void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count); @@ -110,11 +110,11 @@ extern void cx25821_video_wakeup(struct cx25821_dev *dev, extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm); #endif -extern int res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, +extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit); -extern int res_check(struct cx25821_fh *fh, unsigned int bit); -extern int res_locked(struct cx25821_dev *dev, unsigned int bit); -extern void res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, +extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit); +extern int cx25821_res_locked(struct cx25821_dev *dev, unsigned int bit); +extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits); extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input); extern int cx25821_start_video_dma(struct cx25821_dev *dev, @@ -128,67 +128,67 @@ extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status); extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num); extern int cx25821_video_register(struct cx25821_dev *dev, int chan_num, struct video_device *video_template); -extern int get_format_size(void); +extern int cx25821_get_format_size(void); -extern int buffer_setup(struct videobuf_queue *q, unsigned int *count, +extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size); -extern int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, +extern int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field); -extern void buffer_release(struct videobuf_queue *q, +extern void cx25821_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb); extern struct videobuf_queue *get_queue(struct cx25821_fh *fh); -extern int get_resource(struct cx25821_fh *fh, int resource); -extern int video_mmap(struct file *file, struct vm_area_struct *vma); -extern int vidioc_try_fmt_vid_cap(struct file *file, void *priv, +extern int cx25821_get_resource(struct cx25821_fh *fh, int resource); +extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma); +extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); -extern int vidioc_querycap(struct file *file, void *priv, +extern int cx25821_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap); -extern int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, +extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f); -extern int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf); -extern int vidioc_reqbufs(struct file *file, void *priv, +extern int cx25821_vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf); +extern int cx25821_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p); -extern int vidioc_querybuf(struct file *file, void *priv, +extern int cx25821_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p); -extern int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p); -extern int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms); +extern int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p); +extern int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms); extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i); -extern int vidioc_enum_input(struct file *file, void *priv, +extern int cx25821_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i); -extern int vidioc_g_input(struct file *file, void *priv, unsigned int *i); -extern int vidioc_s_input(struct file *file, void *priv, unsigned int i); -extern int vidioc_g_ctrl(struct file *file, void *priv, +extern int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i); +extern int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i); +extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctl); -extern int vidioc_g_fmt_vid_cap(struct file *file, void *priv, +extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); -extern int vidioc_g_frequency(struct file *file, void *priv, +extern int cx25821_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f); extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f); -extern int vidioc_s_frequency(struct file *file, void *priv, +extern int cx25821_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f); -extern int vidioc_g_register(struct file *file, void *fh, +extern int cx25821_vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg); -extern int vidioc_s_register(struct file *file, void *fh, +extern int cx25821_vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg); -extern int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t); -extern int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t); +extern int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t); +extern int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t); -extern int is_valid_width(u32 width, v4l2_std_id tvnorm); -extern int is_valid_height(u32 height, v4l2_std_id tvnorm); +extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm); +extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm); -extern int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p); -extern int vidioc_s_priority(struct file *file, void *f, +extern int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p); +extern int cx25821_vidioc_s_priority(struct file *file, void *f, enum v4l2_priority prio); -extern int vidioc_queryctrl(struct file *file, void *priv, +extern int cx25821_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qctrl); extern int cx25821_set_control(struct cx25821_dev *dev, struct v4l2_control *ctrl, int chan_num); -extern int vidioc_cropcap(struct file *file, void *fh, +extern int cx25821_vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap); -extern int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop); -extern int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop); +extern int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop); +extern int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop); -extern int vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm); +extern int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm); #endif diff --git a/drivers/staging/cx25821/cx25821-video0.c b/drivers/staging/cx25821/cx25821-video0.c index ad7a69129118..0be2cc15d856 100644 --- a/drivers/staging/cx25821/cx25821-video0.c +++ b/drivers/staging/cx25821/cx25821-video0.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -147,7 +147,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO0)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO0)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -165,7 +165,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO0)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -207,19 +207,19 @@ static int video_release(struct file *file) cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO0)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO0); + cx25821_res_free(dev, fh, RESOURCE_VIDEO0); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -239,7 +239,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO0)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO0)))) { return -EBUSY; } @@ -257,11 +257,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO0); + res = cx25821_get_resource(fh, RESOURCE_VIDEO0); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -274,13 +274,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int pix_format = PIXEL_FRMT_422; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -289,11 +289,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; // check if width and height is valid based on set standard - if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { fh->width = f->fmt.pix.width; } - if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { fh->height = f->fmt.pix.height; } @@ -363,7 +363,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -378,50 +378,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-video1.c b/drivers/staging/cx25821/cx25821-video1.c index e3f3c4ac7908..b0bae627bfb1 100644 --- a/drivers/staging/cx25821/cx25821-video1.c +++ b/drivers/staging/cx25821/cx25821-video1.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -147,7 +147,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO1)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO1)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -165,7 +165,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO1)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO1)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -207,19 +207,19 @@ static int video_release(struct file *file) cx_write(channel1->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO1)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO1)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO1); + cx25821_res_free(dev, fh, RESOURCE_VIDEO1); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -239,7 +239,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO1)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO1)))) { return -EBUSY; } @@ -257,11 +257,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO1); + res = cx25821_get_resource(fh, RESOURCE_VIDEO1); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -274,13 +274,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int pix_format = 0; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -289,11 +289,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; // check if width and height is valid based on set standard - if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { fh->width = f->fmt.pix.width; } - if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { fh->height = f->fmt.pix.height; } @@ -363,7 +363,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -378,50 +378,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-video2.c b/drivers/staging/cx25821/cx25821-video2.c index 36fb855a497e..400cdb80674e 100644 --- a/drivers/staging/cx25821/cx25821-video2.c +++ b/drivers/staging/cx25821/cx25821-video2.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -147,7 +147,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO2)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO2)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -165,7 +165,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO2)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO2)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -207,19 +207,19 @@ static int video_release(struct file *file) cx_write(channel2->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO2)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO2)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO2); + cx25821_res_free(dev, fh, RESOURCE_VIDEO2); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -239,7 +239,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO2)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO2)))) { return -EBUSY; } @@ -257,11 +257,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO2); + res = cx25821_get_resource(fh, RESOURCE_VIDEO2); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -274,13 +274,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int pix_format = 0; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -289,11 +289,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; // check if width and height is valid based on set standard - if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { fh->width = f->fmt.pix.width; } - if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { fh->height = f->fmt.pix.height; } @@ -365,7 +365,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -380,50 +380,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-video3.c b/drivers/staging/cx25821/cx25821-video3.c index 1e0f10abdbcd..3b216ed0906e 100644 --- a/drivers/staging/cx25821/cx25821-video3.c +++ b/drivers/staging/cx25821/cx25821-video3.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -147,7 +147,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO3)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO3)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -165,7 +165,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO3)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO3)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -207,19 +207,19 @@ static int video_release(struct file *file) cx_write(channel3->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO3)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO3)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO3); + cx25821_res_free(dev, fh, RESOURCE_VIDEO3); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -239,7 +239,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO3)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO3)))) { return -EBUSY; } @@ -257,11 +257,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO3); + res = cx25821_get_resource(fh, RESOURCE_VIDEO3); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -274,13 +274,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int pix_format = 0; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -289,11 +289,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; // check if width and height is valid based on set standard - if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { fh->width = f->fmt.pix.width; } - if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { fh->height = f->fmt.pix.height; } @@ -364,7 +364,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -379,50 +379,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-video4.c b/drivers/staging/cx25821/cx25821-video4.c index 0cbe7a79d8c0..f7b08c51868a 100644 --- a/drivers/staging/cx25821/cx25821-video4.c +++ b/drivers/staging/cx25821/cx25821-video4.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -146,7 +146,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO4)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO4)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -164,7 +164,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO4)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO4)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -206,19 +206,19 @@ static int video_release(struct file *file) cx_write(channel4->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO4)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO4)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO4); + cx25821_res_free(dev, fh, RESOURCE_VIDEO4); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -238,7 +238,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO4)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO4)))) { return -EBUSY; } @@ -256,11 +256,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO4); + res = cx25821_get_resource(fh, RESOURCE_VIDEO4); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -274,12 +274,12 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, // check priority if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -288,11 +288,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; // check if width and height is valid based on set standard - if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { fh->width = f->fmt.pix.width; } - if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { fh->height = f->fmt.pix.height; } @@ -363,7 +363,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -378,50 +378,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-video5.c b/drivers/staging/cx25821/cx25821-video5.c index 5dc08adc12e8..59370337b076 100644 --- a/drivers/staging/cx25821/cx25821-video5.c +++ b/drivers/staging/cx25821/cx25821-video5.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -147,7 +147,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO5)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO5)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -165,7 +165,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO5)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO5)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -207,19 +207,19 @@ static int video_release(struct file *file) cx_write(channel5->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO5)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO5)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO5); + cx25821_res_free(dev, fh, RESOURCE_VIDEO5); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -239,7 +239,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO5)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO5)))) { return -EBUSY; } @@ -257,11 +257,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO5); + res = cx25821_get_resource(fh, RESOURCE_VIDEO5); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -274,13 +274,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int pix_format = 0; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -289,11 +289,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; // check if width and height is valid based on set standard - if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { fh->width = f->fmt.pix.width; } - if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { fh->height = f->fmt.pix.height; } @@ -363,7 +363,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -378,50 +378,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-video6.c b/drivers/staging/cx25821/cx25821-video6.c index 2938ad3ad3c5..4db2eb83d35a 100644 --- a/drivers/staging/cx25821/cx25821-video6.c +++ b/drivers/staging/cx25821/cx25821-video6.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -147,7 +147,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO6)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO6)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -165,7 +165,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO6)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO6)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -207,18 +207,18 @@ static int video_release(struct file *file) cx_write(channel6->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO6)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO6)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO6); + cx25821_res_free(dev, fh, RESOURCE_VIDEO6); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -238,7 +238,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO6)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO6)))) { return -EBUSY; } @@ -256,11 +256,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO6); + res = cx25821_get_resource(fh, RESOURCE_VIDEO6); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -273,13 +273,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int pix_format = 0; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -288,11 +288,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; // check if width and height is valid based on set standard - if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { fh->width = f->fmt.pix.width; } - if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { fh->height = f->fmt.pix.height; } @@ -363,7 +363,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -378,50 +378,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-video7.c b/drivers/staging/cx25821/cx25821-video7.c index 458e525d72af..5e4a769badad 100644 --- a/drivers/staging/cx25821/cx25821-video7.c +++ b/drivers/staging/cx25821/cx25821-video7.c @@ -85,10 +85,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -146,7 +146,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO7)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO7)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -164,7 +164,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO7)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO7)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -206,19 +206,19 @@ static int video_release(struct file *file) cx_write(channel7->dma_ctl, 0); /* FIFO and RISC disable */ /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO7)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO7)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO7); + cx25821_res_free(dev, fh, RESOURCE_VIDEO7); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -238,7 +238,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO7)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO7)))) { return -EBUSY; } @@ -256,11 +256,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO7); + res = cx25821_get_resource(fh, RESOURCE_VIDEO7); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -273,13 +273,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int pix_format = 0; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -288,11 +288,11 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; // check if width and height is valid based on set standard - if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { fh->width = f->fmt.pix.width; } - if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { fh->height = f->fmt.pix.height; } @@ -362,7 +362,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -377,50 +377,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-videoioctl.c b/drivers/staging/cx25821/cx25821-videoioctl.c index 1da52b54a454..d16807d88be0 100644 --- a/drivers/staging/cx25821/cx25821-videoioctl.c +++ b/drivers/staging/cx25821/cx25821-videoioctl.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -145,7 +145,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO_IOCTL)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO_IOCTL)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -163,7 +163,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO_IOCTL)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO_IOCTL)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -189,19 +189,19 @@ static int video_release(struct file *file) struct cx25821_dev *dev = fh->dev; /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO_IOCTL)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO_IOCTL)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO_IOCTL); + cx25821_res_free(dev, fh, RESOURCE_VIDEO_IOCTL); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -222,7 +222,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO_IOCTL)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO_IOCTL)))) { return -EBUSY; } @@ -240,11 +240,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO_IOCTL); + res = cx25821_get_resource(fh, RESOURCE_VIDEO_IOCTL); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -256,13 +256,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -409,7 +409,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -424,50 +424,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl_set, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-vidups10.c b/drivers/staging/cx25821/cx25821-vidups10.c index b76d9f62c3d1..c746a17ccbd2 100644 --- a/drivers/staging/cx25821/cx25821-vidups10.c +++ b/drivers/staging/cx25821/cx25821-vidups10.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -143,7 +143,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO10)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO10)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -161,7 +161,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO10)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO10)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -189,19 +189,19 @@ static int video_release(struct file *file) //cx_write(channel10->dma_ctl, 0); /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO10)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO10)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO10); + cx25821_res_free(dev, fh, RESOURCE_VIDEO10); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -222,7 +222,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO10)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO10)))) { return -EBUSY; } @@ -240,11 +240,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO10); + res = cx25821_get_resource(fh, RESOURCE_VIDEO10); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -299,13 +299,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -347,7 +347,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -362,50 +362,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl_upstream10, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/cx25821/cx25821-vidups9.c b/drivers/staging/cx25821/cx25821-vidups9.c index 1580da3b29aa..466e0f34ae34 100644 --- a/drivers/staging/cx25821/cx25821-vidups9.c +++ b/drivers/staging/cx25821/cx25821-vidups9.c @@ -86,10 +86,10 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .buf_release = cx25821_buffer_release, }; static int video_open(struct file *file) @@ -143,7 +143,7 @@ static ssize_t video_read(struct file *file, char __user * data, size_t count, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO9)) + if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO9)) return -EBUSY; return videobuf_read_one(&fh->vidq, data, count, ppos, @@ -161,7 +161,7 @@ static unsigned int video_poll(struct file *file, struct cx25821_fh *fh = file->private_data; struct cx25821_buffer *buf; - if (res_check(fh, RESOURCE_VIDEO9)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO9)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; @@ -189,19 +189,19 @@ static int video_release(struct file *file) //cx_write(channel9->dma_ctl, 0); /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO9)) { + if (cx25821_res_check(fh, RESOURCE_VIDEO9)) { videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO9); + cx25821_res_free(dev, fh, RESOURCE_VIDEO9); } if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); kfree(fh->vidq.read_buf); } videobuf_mmap_free(&fh->vidq); - v4l2_prio_close(&dev->prio, &fh->prio); + v4l2_prio_close(&dev->prio, fh->prio); file->private_data = NULL; kfree(fh); @@ -222,7 +222,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO9)))) { + if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO9)))) { return -EBUSY; } @@ -240,11 +240,11 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) if (i != fh->type) return -EINVAL; - res = get_resource(fh, RESOURCE_VIDEO9); + res = cx25821_get_resource(fh, RESOURCE_VIDEO9); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; - res_free(dev, fh, res); + cx25821_res_free(dev, fh, res); return 0; } @@ -299,13 +299,13 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_vid_cap(file, priv, f); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -345,7 +345,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, struct cx25821_fh *fh = priv; int err; if (fh) { - err = v4l2_prio_check(&dev->prio, &fh->prio); + err = v4l2_prio_check(&dev->prio, fh->prio); if (0 != err) return err; } @@ -360,50 +360,50 @@ static const struct v4l2_file_operations video_fops = { .release = video_release, .read = video_read, .poll = video_poll, - .mmap = video_mmap, + .mmap = cx25821_video_mmap, .ioctl = video_ioctl_upstream9, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, #ifdef TUNER_FLAG - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, #endif - .vidioc_cropcap = vidioc_cropcap, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = vidioc_g_priority, - .vidioc_s_priority = vidioc_s_priority, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, #ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, + .vidiocgmbuf = cx25821_vidiocgmbuf, #endif #ifdef TUNER_FLAG - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, #endif }; diff --git a/drivers/staging/netwave/netwave_cs.c b/drivers/staging/netwave/netwave_cs.c index 3875a722d12b..f1ee2cbc8407 100644 --- a/drivers/staging/netwave/netwave_cs.c +++ b/drivers/staging/netwave/netwave_cs.c @@ -61,7 +61,6 @@ #include <pcmcia/cistpl.h> #include <pcmcia/cisreg.h> #include <pcmcia/ds.h> -#include <pcmcia/mem_op.h> #include <asm/system.h> #include <asm/io.h> @@ -382,10 +381,6 @@ static int netwave_probe(struct pcmcia_device *link) link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */ link->io.IOAddrLines = 5; - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = &netwave_interrupt; - /* General socket configuration */ link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; @@ -732,7 +727,7 @@ static int netwave_pcmcia_config(struct pcmcia_device *link) { * Now allocate an interrupt line. Note that this does not * actually assign a handler to the interrupt. */ - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, netwave_interrupt); if (ret) goto failed; @@ -767,7 +762,7 @@ static int netwave_pcmcia_config(struct pcmcia_device *link) { ramBase = ioremap(req.Base, 0x8000); priv->ramBase = ramBase; - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; SET_NETDEV_DEV(dev, &link->dev); diff --git a/drivers/staging/tm6000/Kconfig b/drivers/staging/tm6000/Kconfig new file mode 100644 index 000000000000..5fe759cc2ee9 --- /dev/null +++ b/drivers/staging/tm6000/Kconfig @@ -0,0 +1,32 @@ +config VIDEO_TM6000 + tristate "TV Master TM5600/6000/6010 driver" + depends on VIDEO_DEV && I2C && INPUT && USB && EXPERIMENTAL + select VIDEO_TUNER + select TUNER_XC2028 + select VIDEOBUF_VMALLOC + help + Support for TM5600/TM6000/TM6010 USB Device + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the usb bus, so you need + an external software decoder to watch TV on your computer. + + Say Y if you own such a device and want to use it. + +config VIDEO_TM6000_ALSA + tristate "TV Master TM5600/6000/6010 audio support" + depends on VIDEO_TM6000 && SND && EXPERIMENTAL + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio for + TM5600/TM6000/TM6010 USB Devices. + + To compile this driver as a module, choose M here: the + module will be called tm6000-alsa. + +config VIDEO_TM6000_DVB + bool "DVB Support for tm6000 based TV cards" + depends on VIDEO_TM6000 && DVB_CORE && EXPERIMENTAL + select DVB_ZL10353 + ---help--- + This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/drivers/staging/tm6000/Makefile b/drivers/staging/tm6000/Makefile new file mode 100644 index 000000000000..93370fccc073 --- /dev/null +++ b/drivers/staging/tm6000/Makefile @@ -0,0 +1,17 @@ +tm6000-objs := tm6000-cards.o \ + tm6000-core.o \ + tm6000-i2c.o \ + tm6000-video.o \ + tm6000-stds.o + +ifeq ($(CONFIG_VIDEO_TM6000_DVB),y) +tm6000-objs += tm6000-dvb.o +endif + +obj-$(CONFIG_VIDEO_TM6000) += tm6000.o +obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o + +EXTRA_CFLAGS = -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/staging/tm6000/README b/drivers/staging/tm6000/README new file mode 100644 index 000000000000..c340ebc2ee9f --- /dev/null +++ b/drivers/staging/tm6000/README @@ -0,0 +1,22 @@ +Todo: + - Fix the loss of some blocks when receiving the video URB's + - Add a lock at tm6000_read_write_usb() to prevent two simultaneous access to the + URB control transfers + - Properly add the locks at tm6000-video + - Add audio support + - Add vbi support + - Add IR support + - Do several cleanups + - I think that frame1/frame0 are inverted. This causes a funny effect at the image. + the fix is trivial, but require some tests + - My tm6010 devices sometimes insist on stop working. I need to turn them off, removing + from my machine and wait for a while for it to work again. I'm starting to think that + it is an overheat issue - is there a workaround that we could do? + - Sometimes, tm6010 doesn't read eeprom at the proper time (hardware bug). So, the device + got miss-detected as a "generic" tm6000. This can be really bad if the tuner is the + Low Power one, as it may result on loading the high power firmware, that could damage + the device. Maybe we may read eeprom to double check, when the device is marked as "generic" + - Coding Style fixes + - sparse cleanups + +Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/staging/tm6000/tm6000-alsa.c new file mode 100644 index 000000000000..bc89f9d28002 --- /dev/null +++ b/drivers/staging/tm6000/tm6000-alsa.c @@ -0,0 +1,414 @@ +/* + * + * Support for audio capture for tm5600/6000/6010 + * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com> + * + * Based on cx88-alsa.c + * + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/usb.h> + +#include <asm/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/initval.h> + + +#include "tm6000.h" +#include "tm6000-regs.h" + +#undef dprintk + +#define dprintk(level, fmt, arg...) do { \ + if (debug >= level) \ + printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ + } while (0) + +/**************************************************************************** + Data type declarations - Can be moded to a header file later + ****************************************************************************/ + +struct snd_tm6000_card { + struct snd_card *card; + + spinlock_t reg_lock; + + atomic_t count; + + unsigned int period_size; + unsigned int num_periods; + + struct tm6000_core *core; + struct tm6000_buffer *buf; + + int bufsize; + + struct snd_pcm_substream *substream; +}; + + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); + + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Trident,tm5600}," + "{{Trident,tm6000}," + "{{Trident,tm6010}"); +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +/**************************************************************************** + Module specific funtions + ****************************************************************************/ + +/* + * BOARD Specific: Sets audio DMA + */ + +static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + int val; + + /* Enables audio */ + val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0); + val |= 0x20; + tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + + tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0x80); + + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + int val; + dprintk(1, "Stopping audio DMA\n"); + + /* Enables audio */ + val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0); + val &= ~0x20; + tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + + tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0); + + return 0; +} + +static int dsp_buffer_free(struct snd_tm6000_card *chip) +{ + BUG_ON(!chip->bufsize); + + dprintk(2, "Freeing buffer\n"); + + /* FIXME: Frees buffer */ + + chip->bufsize = 0; + + return 0; +} + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#define DEFAULT_FIFO_SIZE 4096 + +static struct snd_pcm_hardware snd_tm6000_digital_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = DEFAULT_FIFO_SIZE/4, + .period_bytes_max = DEFAULT_FIFO_SIZE/4, + .periods_min = 1, + .periods_max = 1024, + .buffer_bytes_max = (1024*1024), +}; + +/* + * audio pcm capture open callback + */ +static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_tm6000_digital_hw; + + return 0; +_error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_tm6000_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* + * hw_params callback + */ +static int snd_tm6000_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + chip->period_size = params_period_bytes(hw_params); + chip->num_periods = params_periods(hw_params); + chip->bufsize = chip->period_size * params_periods(hw_params); + + BUG_ON(!chip->bufsize); + + dprintk(1, "Setting buffer\n"); + + /* FIXME: Allocate buffer for audio */ + + + return 0; +} + +/* + * hw free callback + */ +static int snd_tm6000_hw_free(struct snd_pcm_substream *substream) +{ + + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + return 0; +} + +/* + * prepare callback + */ +static int snd_tm6000_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + + +/* + * trigger callback + */ +static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + int err; + + spin_lock(&chip->reg_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = _tm6000_start_audio_dma(chip); + break; + case SNDRV_PCM_TRIGGER_STOP: + err = _tm6000_stop_audio_dma(chip); + break; + default: + err = -EINVAL; + break; + } + + spin_unlock(&chip->reg_lock); + + return err; +} + +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + u16 count; + + count = atomic_read(&chip->count); + + return runtime->period_size * (count & (runtime->periods-1)); +} + +/* + * operators + */ +static struct snd_pcm_ops snd_tm6000_pcm_ops = { + .open = snd_tm6000_pcm_open, + .close = snd_tm6000_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_tm6000_hw_params, + .hw_free = snd_tm6000_hw_free, + .prepare = snd_tm6000_prepare, + .trigger = snd_tm6000_card_trigger, + .pointer = snd_tm6000_pointer, +}; + +/* + * create a PCM device + */ +static int __devinit snd_tm6000_pcm(struct snd_tm6000_card *chip, + int device, char *name) +{ + int err; + struct snd_pcm *pcm; + + err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); + if (err < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, name); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); + + return 0; +} + +/* FIXME: Control interface - How to control volume/mute? */ + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * Alsa Constructor - Component probe + */ + +int tm6000_audio_init(struct tm6000_core *dev, int idx) +{ + struct snd_card *card; + struct snd_tm6000_card *chip; + int rc, len; + char component[14]; + + if (idx >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[idx]) + return -ENOENT; + + rc = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card); + if (rc < 0) { + snd_printk(KERN_ERR "cannot create card instance %d\n", idx); + return rc; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + rc = -ENOMEM; + goto error; + } + + chip->core = dev; + chip->card = card; + + strcpy(card->driver, "tm6000-alsa"); + sprintf(component, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + snd_component_add(card, component); + + if (dev->udev->descriptor.iManufacturer) + len = usb_string(dev->udev, + dev->udev->descriptor.iManufacturer, + card->longname, sizeof(card->longname)); + else + len = 0; + + if (len > 0) + strlcat(card->longname, " ", sizeof(card->longname)); + + strlcat(card->longname, card->shortname, sizeof(card->longname)); + + len = strlcat(card->longname, " at ", sizeof(card->longname)); + + if (len < sizeof(card->longname)) + usb_make_path(dev->udev, card->longname + len, + sizeof(card->longname) - len); + + strlcat(card->longname, + dev->udev->speed == USB_SPEED_LOW ? ", low speed" : + dev->udev->speed == USB_SPEED_FULL ? ", full speed" : + ", high speed", + sizeof(card->longname)); + + rc = snd_tm6000_pcm(chip, 0, "tm6000 Digital"); + if (rc < 0) + goto error; + + rc = snd_card_register(card); + if (rc < 0) + goto error; + + + return 0; + +error: + snd_card_free(card); + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_audio_init); + diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c new file mode 100644 index 000000000000..6143e20d139d --- /dev/null +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -0,0 +1,973 @@ +/* + tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/usb.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include <media/tvaudio.h> +#include <media/i2c-addr.h> + +#include "tm6000.h" +#include "tm6000-regs.h" +#include "tuner-xc2028.h" +#include "xc5000.h" + +#define TM6000_BOARD_UNKNOWN 0 +#define TM5600_BOARD_GENERIC 1 +#define TM6000_BOARD_GENERIC 2 +#define TM6010_BOARD_GENERIC 3 +#define TM5600_BOARD_10MOONS_UT821 4 +#define TM5600_BOARD_10MOONS_UT330 5 +#define TM6000_BOARD_ADSTECH_DUAL_TV 6 +#define TM6000_BOARD_FREECOM_AND_SIMILAR 7 +#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8 +#define TM6010_BOARD_HAUPPAUGE_900H 9 +#define TM6010_BOARD_BEHOLD_WANDER 10 +#define TM6010_BOARD_BEHOLD_VOYAGER 11 +#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 +#define TM6010_BOARD_TWINHAN_TU501 13 + +#define TM6000_MAXBOARDS 16 +static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); + +static unsigned long tm6000_devused; + + +struct tm6000_board { + char *name; + + struct tm6000_capabilities caps; + + enum tm6000_devtype type; /* variant of the chipset */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + int demod_addr; /* demodulator address */ + + struct tm6000_gpio gpio; +}; + +struct tm6000_board tm6000_boards[] = { + [TM6000_BOARD_UNKNOWN] = { + .name = "Unknown tm6000 video grabber", + .caps = { + .has_tuner = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + }, + [TM5600_BOARD_GENERIC] = { + .name = "Generic tm5600 board", + .type = TM5600, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + }, + [TM6000_BOARD_GENERIC] = { + .name = "Generic tm6000 board", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + }, + [TM6010_BOARD_GENERIC] = { + .name = "Generic tm6010 board", + .type = TM6010, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + }, + [TM5600_BOARD_10MOONS_UT821] = { + .name = "10Moons UT 821", + .tuner_type = TUNER_XC2028, + .type = TM5600, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + }, + [TM5600_BOARD_10MOONS_UT330] = { + .name = "10Moons UT 330", + .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + }, + }, + [TM6000_BOARD_ADSTECH_DUAL_TV] = { + .name = "ADSTECH Dual TV USB", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_tda9874 = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + }, + [TM6000_BOARD_FREECOM_AND_SIMILAR] = { + .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + }, + [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = { + .name = "ADSTECH Mini Dual TV USB", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc8 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + }, + [TM6010_BOARD_HAUPPAUGE_900H] = { + .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER] = { + .name = "Beholder Wander DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER] = { + .name = "Beholder Voyager TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + }, + [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = { + .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + }, + [TM6010_BOARD_TWINHAN_TU501] = { + .name = "Twinhan TU501(704D1)", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + } +}; + +/* table of devices that work with this driver */ +struct usb_device_id tm6000_id_table[] = { + { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_10MOONS_UT821 }, + { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, + { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, + { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, + { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV }, + { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER }, + { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, + { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { }, +}; + +/* Tuner callback to provide the proper gpio changes needed for xc5000 */ +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC5000) + return 0; + + switch (command) { + case XC5000_TUNER_RESET: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + break; + } + return (rc); +} + + +/* Tuner callback to provide the proper gpio changes needed for xc2028 */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028) + return 0; + + switch (command) { + case XC2028_RESET_CLK: + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, arg); + msleep(10); + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + TM6000_GPIO_CLK, 0); + if (rc < 0) + return rc; + msleep(10); + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + TM6000_GPIO_CLK, 1); + break; + case XC2028_TUNER_RESET: + /* Reset codes during load firmware */ + switch (arg) { + case 0: + /* newer tuner can faster reset */ + switch (dev->model) { + case TM5600_BOARD_10MOONS_UT821: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x00); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + break; + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(75); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + break; + default: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(130); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(130); + break; + } + break; + case 1: + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, 0x01); + msleep(10); + break; + + case 2: + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + TM6000_GPIO_CLK, 0); + if (rc < 0) + return rc; + msleep(100); + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + TM6000_GPIO_CLK, 1); + msleep(100); + break; + } + } + return rc; +} + +int tm6000_cards_setup(struct tm6000_core *dev) +{ + int i, rc; + + /* + * Board-specific initialization sequence. Handles all GPIO + * initialization sequences that are board-specific. + * Up to now, all found devices use GPIO1 and GPIO4 at the same way. + * Probably, they're all based on some reference device. Due to that, + * there's a common routine at the end to handle those GPIO's. Devices + * that use different pinups or init sequences can just return at + * the board-specific session. + */ + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + case TM6010_BOARD_GENERIC: + /* Turn xceive 3028 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + /* Turn zarlink zl10353 off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01); + msleep(15); + /* ir ? */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01); + msleep(15); + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); + msleep(15); + /* DVB led off (orange) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_VOYAGER: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + break; + default: + break; + } + + /* + * Default initialization. Most of the devices seem to use GPIO1 + * and GPIO4.on the same way, so, this handles the common sequence + * used by most devices. + * If a device uses a different sequence or different GPIO pins for + * reset, just add the code at the board-specific part + */ + + if (dev->gpio.tuner_reset) { + for (i = 0; i < 2; i++) { + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + + msleep(10); /* Just to be conservative */ + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + msleep(10); + + if (!i) { + rc = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); + if (rc >= 0) + printk(KERN_DEBUG "board=0x%08x\n", rc); + } + } + } else { + printk(KERN_ERR "Tuner reset is not configured\n"); + return -1; + } + + msleep(50); + + return 0; +}; + +static void tm6000_config_tuner(struct tm6000_core *dev) +{ + struct tuner_setup tun_setup; + + /* Load tuner module */ + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", "tuner", dev->tuner_addr, NULL); + + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + + tun_setup.mode_mask = 0; + if (dev->caps.has_tuner) + tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); + if (dev->caps.has_dvb) + tun_setup.mode_mask |= T_DIGITAL_TV; + + switch (dev->tuner_type) { + case TUNER_XC2028: + tun_setup.tuner_callback = tm6000_tuner_callback;; + break; + case TUNER_XC5000: + tun_setup.tuner_callback = tm6000_xc5000_callback; + break; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + ctl.input1 = 1; + ctl.read_not_reliable = 0; + ctl.msleep = 10; + ctl.demod = XC3028_FE_ZARLINK456; + ctl.vhfbw7 = 1; + ctl.uhfbw8 = 1; + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + ctl.fname = "xc3028L-v36.fw"; + break; + default: + if (dev->dev_type == TM6010) + ctl.fname = "xc3028-v27.fw"; + else + ctl.fname = "xc3028-v24.fw"; + } + + printk(KERN_INFO "Setting firmware parameters for xc2028\n"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc2028_cfg); + + } + break; + case TUNER_XC5000: + { + struct v4l2_priv_tun_config xc5000_cfg; + struct xc5000_config ctl = { + .i2c_address = dev->tuner_addr, + .if_khz = 4570, + .radio_input = XC5000_RADIO_FM1, + }; + + xc5000_cfg.tuner = TUNER_XC5000; + xc5000_cfg.priv = &ctl; + + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc5000_cfg); + } + break; + default: + printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n"); + break; + } +} + +static int tm6000_init_dev(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + int rc = 0; + + mutex_init(&dev->lock); + + mutex_lock(&dev->lock); + + /* Initializa board-specific data */ + dev->dev_type = tm6000_boards[dev->model].type; + dev->tuner_type = tm6000_boards[dev->model].tuner_type; + dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; + + dev->gpio = tm6000_boards[dev->model].gpio; + + dev->demod_addr = tm6000_boards[dev->model].demod_addr; + + dev->caps = tm6000_boards[dev->model].caps; + + /* initialize hardware */ + rc = tm6000_init(dev); + if (rc < 0) + goto err; + + rc = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); + if (rc < 0) + goto err; + + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + + /* Default values for STD and resolutions */ + dev->width = 720; + dev->height = 480; + dev->norm = V4L2_STD_PAL_M; + + /* Configure tuner */ + tm6000_config_tuner(dev); + + /* Set video standard */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 3092; /* 193.25 MHz */ + dev->freq = f.frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + if (dev->caps.has_tda9874) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvaudio", "tvaudio", I2C_ADDR_TDA9874, NULL); + + /* register and initialize V4L2 */ + rc = tm6000_v4l2_register(dev); + if (rc < 0) + goto err; + + if (dev->caps.has_dvb) { + dev->dvb = kzalloc(sizeof(*(dev->dvb)), GFP_KERNEL); + if (!dev->dvb) { + rc = -ENOMEM; + goto err2; + } + +#ifdef CONFIG_VIDEO_TM6000_DVB + rc = tm6000_dvb_register(dev); + if (rc < 0) { + kfree(dev->dvb); + dev->dvb = NULL; + goto err2; + } +#endif + } + mutex_unlock(&dev->lock); + return 0; + +err2: + v4l2_device_unregister(&dev->v4l2_dev); + +err: + mutex_unlock(&dev->lock); + return rc; +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) + +static void get_max_endpoint(struct usb_device *udev, + struct usb_host_interface *alt, + char *msgtype, + struct usb_host_endpoint *curr_e, + struct tm6000_endpoint *tm_ep) +{ + u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize); + unsigned int size = tmp & 0x7ff; + + if (udev->speed == USB_SPEED_HIGH) + size = size * hb_mult (tmp); + + if (size > tm_ep->maxsize) { + tm_ep->endp = curr_e; + tm_ep->maxsize = size; + tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber; + tm_ep->bAlternateSetting = alt->desc.bAlternateSetting; + + printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n", + msgtype, curr_e->desc.bEndpointAddress, + size); + } +} + +/* + * tm6000_usb_probe() + * checks for supported devices + */ +static int tm6000_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct tm6000_core *dev = NULL; + int i, rc = 0; + int nr = 0; + char *speed; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + + /* Selects the proper interface */ + rc = usb_set_interface(usbdev, 0, 1); + if (rc < 0) + goto err; + + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS); + if (nr >= TM6000_MAXBOARDS) { + printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS); + usb_put_dev(usbdev); + return -ENOMEM; + } + + /* Create and initialize dev struct */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk(KERN_ERR "tm6000" ": out of memory!\n"); + usb_put_dev(usbdev); + return -ENOMEM; + } + spin_lock_init(&dev->slock); + + /* Increment usage count */ + tm6000_devused |= 1<<nr; + snprintf(dev->name, 29, "tm6000 #%d", nr); + + dev->model = id->driver_info; + if ((card[nr] >= 0) && (card[nr] < ARRAY_SIZE(tm6000_boards))) + dev->model = card[nr]; + + dev->udev = usbdev; + dev->devno = nr; + + switch (usbdev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + + + /* Get endpoints */ + for (i = 0; i < interface->num_altsetting; i++) { + int ep; + + for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { + struct usb_host_endpoint *e; + int dir_out; + + e = &interface->altsetting[i].endpoint[ep]; + + dir_out = ((e->desc.bEndpointAddress & + USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); + + printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n", + i, + interface->altsetting[i].desc.bInterfaceNumber, + interface->altsetting[i].desc.bInterfaceClass); + + switch (e->desc.bmAttributes) { + case USB_ENDPOINT_XFER_BULK: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk IN", e, + &dev->bulk_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk OUT", e, + &dev->bulk_out); + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC IN", e, + &dev->isoc_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC OUT", e, + &dev->isoc_out); + } + break; + } + } + } + + + printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n", + speed, + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct), + interface->altsetting->desc.bInterfaceNumber); + +/* check if the the device has the iso in endpoint at the correct place */ + if (!dev->isoc_in.endp) { + printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); + rc = -ENODEV; + + goto err; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name); + + rc = tm6000_init_dev(dev); + + if (rc < 0) + goto err; + + return 0; + +err: + printk(KERN_ERR "tm6000: Error %d while registering\n", rc); + + tm6000_devused &= ~(1<<nr); + usb_put_dev(usbdev); + + kfree(dev); + return rc; +} + +/* + * tm6000_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void tm6000_usb_disconnect(struct usb_interface *interface) +{ + struct tm6000_core *dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name); + + mutex_lock(&dev->lock); + +#ifdef CONFIG_VIDEO_TM6000_DVB + if (dev->dvb) { + tm6000_dvb_unregister(dev); + kfree(dev->dvb); + } +#endif + + tm6000_v4l2_unregister(dev); + + tm6000_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + dev->state |= DEV_DISCONNECTED; + + usb_put_dev(dev->udev); + + mutex_unlock(&dev->lock); + kfree(dev); +} + +static struct usb_driver tm6000_usb_driver = { + .name = "tm6000", + .probe = tm6000_usb_probe, + .disconnect = tm6000_usb_disconnect, + .id_table = tm6000_id_table, +}; + +static int __init tm6000_module_init(void) +{ + int result; + + printk(KERN_INFO "tm6000" " v4l2 driver version %d.%d.%d loaded\n", + (TM6000_VERSION >> 16) & 0xff, + (TM6000_VERSION >> 8) & 0xff, TM6000_VERSION & 0xff); + + /* register this driver with the USB subsystem */ + result = usb_register(&tm6000_usb_driver); + if (result) + printk(KERN_ERR "tm6000" + " usb_register failed. Error number %d.\n", result); + + return result; +} + +static void __exit tm6000_module_exit(void) +{ + /* deregister at USB subsystem */ + usb_deregister(&tm6000_usb_driver); +} + +module_init(tm6000_module_init); +module_exit(tm6000_module_exit); + +MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/staging/tm6000/tm6000-core.c new file mode 100644 index 000000000000..bfbc53bd2912 --- /dev/null +++ b/drivers/staging/tm6000/tm6000-core.c @@ -0,0 +1,602 @@ +/* + tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + - DVB-T support + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include "tm6000.h" +#include "tm6000-regs.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> + +#define USB_TIMEOUT 5*HZ /* ms */ + +int tm6000_read_write_usb (struct tm6000_core *dev, u8 req_type, u8 req, + u16 value, u16 index, u8 *buf, u16 len) +{ + int ret, i; + unsigned int pipe; + static int ini=0, last=0, n=0; + u8 *data=NULL; + + if (len) + data = kzalloc(len, GFP_KERNEL); + + + if (req_type & USB_DIR_IN) + pipe=usb_rcvctrlpipe(dev->udev, 0); + else { + pipe=usb_sndctrlpipe(dev->udev, 0); + memcpy(data, buf, len); + } + + if (tm6000_debug & V4L2_DEBUG_I2C) { + if (!ini) + last=ini=jiffies; + + printk("%06i (dev %p, pipe %08x): ", n, dev->udev, pipe); + + printk( "%s: %06u ms %06u ms %02x %02x %02x %02x %02x %02x %02x %02x ", + (req_type & USB_DIR_IN)?" IN":"OUT", + jiffies_to_msecs(jiffies-last), + jiffies_to_msecs(jiffies-ini), + req_type, req,value&0xff,value>>8, index&0xff, index>>8, + len&0xff, len>>8); + last=jiffies; + n++; + + if ( !(req_type & USB_DIR_IN) ) { + printk(">>> "); + for (i=0;i<len;i++) { + printk(" %02x",buf[i]); + } + printk("\n"); + } + } + + ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, data, + len, USB_TIMEOUT); + + if (req_type & USB_DIR_IN) + memcpy(buf, data, len); + + if (tm6000_debug & V4L2_DEBUG_I2C) { + if (ret<0) { + if (req_type & USB_DIR_IN) + printk("<<< (len=%d)\n",len); + + printk("%s: Error #%d\n", __FUNCTION__, ret); + } else if (req_type & USB_DIR_IN) { + printk("<<< "); + for (i=0;i<len;i++) { + printk(" %02x",buf[i]); + } + printk("\n"); + } + } + + kfree(data); + + msleep(5); + + return ret; +} + +int tm6000_set_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + return + tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg); + +int tm6000_get_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[1]; + + rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc<0) + return rc; + + return *buf; +} +EXPORT_SYMBOL_GPL(tm6000_get_reg); + +int tm6000_get_reg16 (struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[2]; + + rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 2); + + if (rc<0) + return rc; + + return buf[1]|buf[0]<<8; +} + +int tm6000_get_reg32 (struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[4]; + + rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 4); + + if (rc<0) + return rc; + + return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; +} + +void tm6000_set_fourcc_format(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + int val; + + val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0) & 0xfc; + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + else + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val | 1); + } else { + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + else + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90); + } +} + +int tm6000_init_analog_mode (struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + int val; + + /* Enable video */ + val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0); + val |= 0x60; + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + val = tm6000_get_reg(dev, + TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0); + val &= ~0x40; + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val); + + /* Init teletext */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); + tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55); + tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01); + tm6000_set_reg(dev, + TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02); + tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35); + tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0); + tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11); + tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); + tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + + + /* Init audio */ + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x05); + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x06); + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08); + tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); + tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14); + tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); + tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32); + tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64); + tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3); + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + + } else { + /* Enables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + + if (dev->scaler) { + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); + } else { + /* Enable Hfilter and disable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); + } + + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f); + + /* AP Software reset */ + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + + tm6000_set_fourcc_format(dev); + + /* Disables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + + /* E3: Select input 0 - TV tuner */ + tm6000_set_reg(dev, TM6010_REQ07_RE3_OUT_SEL1, 0x00); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60); + + /* This controls input */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_2, 0x0); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_3, 0x01); + } + msleep(20); + + /* Tuner firmware can now be loaded */ + + /*FIXME: Hack!!! */ + struct v4l2_frequency f; + mutex_lock(&dev->lock); + f.frequency=dev->freq; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + mutex_unlock(&dev->lock); + + msleep(100); + tm6000_set_standard (dev, &dev->norm); + tm6000_set_audio_bitrate (dev,48000); + + return 0; +} + +int tm6000_init_digital_mode (struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + int val; + u8 buf[2]; + + /* digital init */ + val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0); + val &= ~0x60; + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + val = tm6000_get_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0); + val |= 0x40; + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28); + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); + tm6000_read_write_usb (dev, 0xc0, 0x0e, 0x00c2, 0x0008, buf, 2); + printk (KERN_INFO "buf %#x %#x \n", buf[0], buf[1]); + + + } else { + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c); + tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x37); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); + + tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c); + tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00eb, 0x08); + msleep(50); + + tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(50); + tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); + msleep(50); + tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(100); + } + return 0; +} + +struct reg_init { + u8 req; + u8 reg; + u8 val; +}; + +/* The meaning of those initializations are unknown */ +struct reg_init tm6000_init_tab[] = { + /* REG VALUE */ + { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x1f }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, + { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, + { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23 }, + { TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0x08 }, + { TM6010_REQ07_RE2_OUT_SEL2, 0x00 }, + { TM6010_REQ07_RE3_OUT_SEL1, 0x10 }, + { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0x00 }, + { TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0x00 }, + { REQ_07_SET_GET_AVREG, 0xeb, 0x64 }, /* 48000 bits/sample, external input */ + { REQ_07_SET_GET_AVREG, 0xee, 0xc2 }, + { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */ + { TM6010_REQ05_R18_IMASK7, 0x00 }, +}; + +struct reg_init tm6010_init_tab[] = { + { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 }, + { TM6010_REQ07_RC4_HSTART0, 0xa0 }, + { TM6010_REQ07_RC6_HEND0, 0x40 }, + { TM6010_REQ07_RCA_VEND0, 0x31 }, + { TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0xe1 }, + { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 }, + { TM6010_REQ07_RFE_POWER_DOWN, 0x7f }, + + { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 }, + { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 }, + { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 }, + { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 }, + { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 }, + { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, + { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, + { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, + { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + + { TM6010_REQ05_R18_IMASK7, 0x00 }, + + { TM6010_REQ07_RD8_IR_LEADER1, 0xaa }, + { TM6010_REQ07_RD8_IR_LEADER0, 0x30 }, + { TM6010_REQ07_RD8_IR_PULSE_CNT1, 0x20 }, + { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0xd0 }, + { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 }, + { TM6010_REQ07_RD8_IR, 0x2f }, + + /* set remote wakeup key:any key wakeup */ + { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe }, + { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0xff }, +}; + +int tm6000_init (struct tm6000_core *dev) +{ + int board, rc=0, i, size; + struct reg_init *tab; + + if (dev->dev_type == TM6010) { + tab = tm6010_init_tab; + size = ARRAY_SIZE(tm6010_init_tab); + } else { + tab = tm6000_init_tab; + size = ARRAY_SIZE(tm6000_init_tab); + } + + /* Load board's initialization table */ + for (i=0; i< size; i++) { + rc= tm6000_set_reg (dev, tab[i].req, tab[i].reg, tab[i].val); + if (rc<0) { + printk (KERN_ERR "Error %i while setting req %d, " + "reg %d to value %d\n", rc, + tab[i].req,tab[i].reg, tab[i].val); + return rc; + } + } + + msleep(5); /* Just to be conservative */ + + /* Check board version - maybe 10Moons specific */ + board=tm6000_get_reg32 (dev, REQ_40_GET_VERSION, 0, 0); + if (board >=0) { + printk (KERN_INFO "Board version = 0x%08x\n",board); + } else { + printk (KERN_ERR "Error %i while retrieving board version\n",board); + } + + rc = tm6000_cards_setup(dev); + + return rc; +} + +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) +{ + int val; + + val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0); +printk("Original value=%d\n",val); + if (val<0) + return val; + + val &= 0x0f; /* Preserve the audio input control bits */ + switch (bitrate) { + case 44100: + val|=0xd0; + dev->audio_bitrate=bitrate; + break; + case 48000: + val|=0x60; + dev->audio_bitrate=bitrate; + break; + } + val=tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, val); + + return val; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); diff --git a/drivers/staging/tm6000/tm6000-dvb.c b/drivers/staging/tm6000/tm6000-dvb.c new file mode 100644 index 000000000000..eafc89c22b6b --- /dev/null +++ b/drivers/staging/tm6000/tm6000-dvb.c @@ -0,0 +1,346 @@ +/* + tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/usb.h> + +#include "tm6000.h" +#include "tm6000-regs.h" + +#include "zl10353.h" + +#include <media/tuner.h> + +#include "tuner-xc2028.h" + +static void inline print_err_status (struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch(status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet<0) { + dprintk(dev, 1, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, 1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static void tm6000_urb_received(struct urb *urb) +{ + int ret; + struct tm6000_core* dev = urb->context; + + if(urb->status != 0) { + print_err_status (dev,0,urb->status); + } + else if(urb->actual_length>0){ + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + } + + if(dev->dvb->streams > 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if(ret < 0) { + printk(KERN_ERR "tm6000: error %s\n", __FUNCTION__); + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + } +} + +int tm6000_start_stream(struct tm6000_core *dev) +{ + int ret; + unsigned int pipe, size; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got start stream request %s\n",__FUNCTION__); + + tm6000_init_digital_mode(dev); + + dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if(dvb->bulk_urb == NULL) { + printk(KERN_ERR "tm6000: couldn't allocate urb\n"); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + size = size * 15; /* 512 x 8 or 12 or 15 */ + + dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); + if(dvb->bulk_urb->transfer_buffer == NULL) { + usb_free_urb(dvb->bulk_urb); + printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n"); + return -ENOMEM; + } + + usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, + dvb->bulk_urb->transfer_buffer, + size, + tm6000_urb_received, dev); + + ret = usb_clear_halt(dev->udev, pipe); + if(ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n",ret,__FUNCTION__); + return ret; + } + else { + printk(KERN_ERR "tm6000: pipe resetted\n"); + } + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + ret = usb_submit_urb(dvb->bulk_urb, GFP_KERNEL); + +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ + if (ret) { + printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n",ret); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + return ret; + } + + return 0; +} + +void tm6000_stop_stream(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if(dvb->bulk_urb) { + printk (KERN_INFO "urb killing\n"); + usb_kill_urb(dvb->bulk_urb); + printk (KERN_INFO "urb buffer free\n"); + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + } +} + +int tm6000_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + printk(KERN_INFO "tm6000: got start feed request %s\n",__FUNCTION__); + + mutex_lock(&dvb->mutex); + if(dvb->streams == 0) { + dvb->streams = 1; +/* mutex_init(&tm6000_dev->streming_mutex); */ + tm6000_start_stream(dev); + } + else { + ++(dvb->streams); + } + mutex_unlock(&dvb->mutex); + + return 0; +} + +int tm6000_stop_feed(struct dvb_demux_feed *feed) { + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got stop feed request %s\n",__FUNCTION__); + + mutex_lock(&dvb->mutex); + + printk (KERN_INFO "stream %#x\n", dvb->streams); + --(dvb->streams); + if(dvb->streams == 0) { + printk (KERN_INFO "stop stream\n"); + tm6000_stop_stream(dev); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + } + mutex_unlock(&dvb->mutex); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + + return 0; +} + +int tm6000_dvb_attach_frontend(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if(dev->caps.has_zl10353) { + struct zl10353_config config = + {.demod_address = dev->demod_addr, + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45700, + .disable_i2c_gate_ctrl = 1, + }; + + dvb->frontend = dvb_attach(zl10353_attach, &config, + &dev->i2c_adap); + } + else { + printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); + return -1; + } + + return (!dvb->frontend) ? -1 : 0; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +int tm6000_dvb_register(struct tm6000_core *dev) +{ + int ret = -1; + struct tm6000_dvb *dvb = dev->dvb; + + mutex_init(&dvb->mutex); + + dvb->streams = 0; + + /* attach the frontend */ + ret = tm6000_dvb_attach_frontend(dev); + if(ret < 0) { + printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); + goto err; + } + + ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", + THIS_MODULE, &dev->udev->dev, adapter_nr); + dvb->adapter.priv = dev; + + if (dvb->frontend) { + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_adap, + .i2c_addr = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_tuner_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register " + "frontend (xc3028)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC2028/3028 asked to be " + "attached to frontend!\n"); + } else { + printk(KERN_ERR "tm6000: no frontend found\n"); + } + + dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING + | DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 8; + dvb->demux.feednum = 8; + dvb->demux.start_feed = tm6000_start_feed; + dvb->demux.stop_feed = tm6000_stop_feed; + dvb->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&dvb->demux); + if(ret < 0) { + printk("tm6000: dvb_dmx_init failed (errno = %d)\n", ret); + goto frontend_err; + } + + dvb->dmxdev.filternum = dev->dvb->demux.filternum; + dvb->dmxdev.demux = &dev->dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if(ret < 0) { + printk("tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); + goto dvb_dmx_err; + } + + return 0; + +dvb_dmx_err: + dvb_dmx_release(&dvb->demux); +frontend_err: + if(dvb->frontend) { + dvb_frontend_detach(dvb->frontend); + dvb_unregister_frontend(dvb->frontend); + } +adapter_err: + dvb_unregister_adapter(&dvb->adapter); +err: + return ret; +} + +void tm6000_dvb_unregister(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if(dvb->bulk_urb != NULL) { + struct urb *bulk_urb = dvb->bulk_urb; + + kfree(bulk_urb->transfer_buffer); + bulk_urb->transfer_buffer = NULL; + usb_unlink_urb(bulk_urb); + usb_free_urb(bulk_urb); + } + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + if(dvb->frontend) { + dvb_frontend_detach(dvb->frontend); + dvb_unregister_frontend(dvb->frontend); + } + + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_adapter(&dvb->adapter); + mutex_destroy(&dvb->mutex); +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ + +} diff --git a/drivers/staging/tm6000/tm6000-i2c.c b/drivers/staging/tm6000/tm6000-i2c.c new file mode 100644 index 000000000000..94ff489a1bbb --- /dev/null +++ b/drivers/staging/tm6000/tm6000-i2c.c @@ -0,0 +1,370 @@ +/* + tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + - Fix SMBus Read Byte command + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/i2c.h> + +#include "tm6000.h" +#include "tm6000-regs.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include "tuner-xc2028.h" + + +/*FIXME: Hack to avoid needing to patch i2c-id.h */ +#define I2C_HW_B_TM6000 I2C_HW_B_EM28XX +/* ----------------------------------------------------------- */ + +static unsigned int i2c_debug = 0; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define i2c_dprintk(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __FUNCTION__ , ##args); } while (0) + +static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + unsigned int tsleep; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 64; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | reg << 8, 0, buf, len); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + /* Calculate delay time, 14000us for 64 bytes */ + tsleep = ((len * 200) + 200 + 1000) / 1000; + msleep(tsleep); + + /* release mutex */ + return rc; +} + +/* Generic read - doesn't work fine with 16bit registers */ +static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + u8 b[2]; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 64; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { + /* + * Workaround an I2C bug when reading from zl10353 + */ + reg -= 1; + len += 1; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); + + *buf = b[1]; + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); + } + + /* release mutex */ + return rc; +} + +/* + * read from a 16bit register + * for example xc2028, xc3028 or xc3028L + */ +static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, + __u16 reg, char *buf, int len) +{ + int rc; + unsigned char ureg; + + if (!buf || len != 2) + return -1; + + /* capture mutex */ + if (dev->dev_type == TM6010) { + ureg = reg & 0xFF; + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | (reg & 0xFF00), 0, &ureg, 1); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + msleep(1400 / 1000); + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, + reg, 0, buf, len); + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, + addr, reg, buf, len); + } + + /* release mutex */ + return rc; +} + +static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct tm6000_core *dev = i2c_adap->algo_data; + int addr, rc, i, byte; + + if (num <= 0) + return 0; + for (i = 0; i < num; i++) { + addr = (msgs[i].addr << 1) & 0xff; + i2c_dprintk(2,"%s %s addr=0x%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read request without preceding register selection */ + /* + * The TM6000 only supports a read transaction + * immediately after a 1 or 2 byte write to select + * a register. We cannot fulfil this request. + */ + i2c_dprintk(2, " read without preceding write not" + " supported"); + rc = -EOPNOTSUPP; + goto err; + } else if (i + 1 < num && msgs[i].len <= 2 && + (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* 1 or 2 byte write followed by a read */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + i2c_dprintk(2, "; joined to read %s len=%d:", + i == num - 2 ? "stop" : "nonstop", + msgs[i + 1].len); + + if (msgs[i].len == 2) { + rc = tm6000_i2c_recv_regs16(dev, addr, + msgs[i].buf[0] << 8 | msgs[i].buf[1], + msgs[i + 1].buf, msgs[i + 1].len); + } else { + rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], + msgs[i + 1].buf, msgs[i + 1].len); + } + + i++; + + if (addr == dev->tuner_addr << 1) { + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + } + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } else { + /* write bytes */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], + msgs[i].buf + 1, msgs[i].len - 1); + + if (addr == dev->tuner_addr << 1) { + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + } + } + if (i2c_debug >= 2) + printk("\n"); + if (rc < 0) + goto err; + } + + return num; +err: + i2c_dprintk(2," ERROR: %i\n", rc); + return rc; +} + +static int tm6000_i2c_eeprom(struct tm6000_core *dev, + unsigned char *eedata, int len) +{ + int i, rc; + unsigned char *p = eedata; + unsigned char bytes[17]; + + dev->i2c_client.addr = 0xa0 >> 1; + + bytes[16] = '\0'; + for (i = 0; i < len; ) { + *p = i; + rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); + if (rc < 1) { + if (p == eedata) + goto noeeprom; + else { + printk(KERN_WARNING + "%s: i2c eeprom read error (err=%d)\n", + dev->name, rc); + } + return -1; + } + p++; + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); + printk(" %02x", eedata[i]); + if ((eedata[i] >= ' ') && (eedata[i] <= 'z')) { + bytes[i%16] = eedata[i]; + } else { + bytes[i%16]='.'; + } + + i++; + + if (0 == (i % 16)) { + bytes[16] = '\0'; + printk(" %s\n", bytes); + } + } + if (0 != (i%16)) { + bytes[i%16] = '\0'; + for (i %= 16; i < 16; i++) + printk(" "); + } + printk(" %s\n", bytes); + + return 0; + +noeeprom: + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name, rc); + return rc; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +#define mass_write(addr, reg, data...) \ + { const static u8 _val[] = data; \ + rc=tm6000_read_write_usb(dev,USB_DIR_OUT | USB_TYPE_VENDOR, \ + REQ_16_SET_GET_I2C_WR1_RDN,(reg<<8)+addr, 0x00, (u8 *) _val, \ + ARRAY_SIZE(_val)); \ + if (rc<0) { \ + printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc); \ + return rc; \ + } \ + msleep (10); \ + } + +static struct i2c_algorithm tm6000_algo = { + .master_xfer = tm6000_i2c_xfer, + .functionality = functionality, +}; + +static struct i2c_adapter tm6000_adap_template = { + .owner = THIS_MODULE, + .class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, + .name = "tm6000", + .id = I2C_HW_B_TM6000, + .algo = &tm6000_algo, +}; + +static struct i2c_client tm6000_client_template = { + .name = "tm6000 internal", +}; + +/* ----------------------------------------------------------- */ + +/* + * tm6000_i2c_register() + * register i2c bus + */ +int tm6000_i2c_register(struct tm6000_core *dev) +{ + unsigned char eedata[256]; + + dev->i2c_adap = tm6000_adap_template; + dev->i2c_adap.dev.parent = &dev->udev->dev; + strcpy(dev->i2c_adap.name, dev->name); + dev->i2c_adap.algo_data = dev; + i2c_add_adapter(&dev->i2c_adap); + + dev->i2c_client = tm6000_client_template; + dev->i2c_client.adapter = &dev->i2c_adap; + + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + + tm6000_i2c_eeprom(dev, eedata, sizeof(eedata)); + + return 0; +} + +/* + * tm6000_i2c_unregister() + * unregister i2c_bus + */ +int tm6000_i2c_unregister(struct tm6000_core *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} diff --git a/drivers/staging/tm6000/tm6000-regs.h b/drivers/staging/tm6000/tm6000-regs.h new file mode 100644 index 000000000000..1c5289c971fa --- /dev/null +++ b/drivers/staging/tm6000/tm6000-regs.h @@ -0,0 +1,541 @@ +/* + tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Define TV Master TM5600/TM6000/TM6010 Request codes + */ +#define REQ_00_SET_IR_VALUE 0 +#define REQ_01_SET_WAKEUP_IRCODE 1 +#define REQ_02_GET_IR_CODE 2 +#define REQ_03_SET_GET_MCU_PIN 3 +#define REQ_04_EN_DISABLE_MCU_INT 4 +#define REQ_05_SET_GET_USBREG 5 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_06_SET_GET_USBREG_BIT 6 +#define REQ_07_SET_GET_AVREG 7 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_08_SET_GET_AVREG_BIT 8 +#define REQ_09_SET_GET_TUNER_FQ 9 +#define REQ_10_SET_TUNER_SYSTEM 10 +#define REQ_11_SET_EEPROM_ADDR 11 +#define REQ_12_SET_GET_EEPROMBYTE 12 +#define REQ_13_GET_EEPROM_SEQREAD 13 +#define REQ_14_SET_GET_I2C_WR2_RDN 14 +#define REQ_15_SET_GET_I2CBYTE 15 + /* Write: Subaddr, Slave Addr, value, 0 */ + /* Read : Subaddr, Slave Addr, value, 1 */ +#define REQ_16_SET_GET_I2C_WR1_RDN 16 + /* Subaddr, Slave Addr, 0, length */ +#define REQ_17_SET_GET_I2CFP 17 + /* Write: Slave Addr, register, value */ + /* Read : Slave Addr, register, 2, data */ +#define REQ_20_DATA_TRANSFER 20 +#define REQ_30_I2C_WRITE 30 +#define REQ_31_I2C_READ 31 +#define REQ_35_AFTEK_TUNER_READ 35 +#define REQ_40_GET_VERSION 40 +#define REQ_50_SET_START 50 +#define REQ_51_SET_STOP 51 +#define REQ_52_TRANSMIT_DATA 52 +#define REQ_53_SPI_INITIAL 53 +#define REQ_54_SPI_SETSTART 54 +#define REQ_55_SPI_INOUTDATA 55 +#define REQ_56_SPI_SETSTOP 56 + +/* + * Define TV Master TM5600/TM6000/TM6010 GPIO lines + */ + +#define TM6000_GPIO_CLK 0x101 +#define TM6000_GPIO_DATA 0x100 + +#define TM6000_GPIO_1 0x102 +#define TM6000_GPIO_2 0x103 +#define TM6000_GPIO_3 0x104 +#define TM6000_GPIO_4 0x300 +#define TM6000_GPIO_5 0x301 +#define TM6000_GPIO_6 0x304 +#define TM6000_GPIO_7 0x305 + +/* tm6010 defines GPIO with different values */ +#define TM6010_GPIO_0 0x0102 +#define TM6010_GPIO_1 0x0103 +#define TM6010_GPIO_2 0x0104 +#define TM6010_GPIO_3 0x0105 +#define TM6010_GPIO_4 0x0106 +#define TM6010_GPIO_5 0x0107 +#define TM6010_GPIO_6 0x0300 +#define TM6010_GPIO_7 0x0301 +#define TM6010_GPIO_9 0x0305 +/* + * Define TV Master TM5600/TM6000/TM6010 URB message codes and length + */ + +enum { + TM6000_URB_MSG_VIDEO=1, + TM6000_URB_MSG_AUDIO, + TM6000_URB_MSG_VBI, + TM6000_URB_MSG_PTS, + TM6000_URB_MSG_ERR, +}; + +/* Define TM6000/TM6010 Video decoder registers */ +#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 +#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 +#define TM6010_REQ07_R02_VIDEO_CONTROL2 0x07, 0x02 +#define TM6010_REQ07_R03_YC_SEP_CONTROL 0x07, 0x03 +#define TM6010_REQ07_R04_LUMA_HAGC_CONTROL 0x07, 0x04 +#define TM6010_REQ07_R05_NOISE_THRESHOLD 0x07, 0x05 +#define TM6010_REQ07_R06_AGC_GATE_THRESHOLD 0x07, 0x06 +#define TM6010_REQ07_R07_OUTPUT_CONTROL 0x07, 0x07 +#define TM6010_REQ07_R08_LUMA_CONTRAST_ADJ 0x07, 0x08 +#define TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ 0x07, 0x09 +#define TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ 0x07, 0x0a +#define TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ 0x07, 0x0b +#define TM6010_REQ07_R0C_CHROMA_AGC_CONTROL 0x07, 0x0c +#define TM6010_REQ07_R0D_CHROMA_KILL_LEVEL 0x07, 0x0d +#define TM6010_REQ07_R0F_CHROMA_AUTO_POSITION 0x07, 0x0f +#define TM6010_REQ07_R10_AGC_PEAK_NOMINAL 0x07, 0x10 +#define TM6010_REQ07_R11_AGC_PEAK_CONTROL 0x07, 0x11 +#define TM6010_REQ07_R12_AGC_GATE_STARTH 0x07, 0x12 +#define TM6010_REQ07_R13_AGC_GATE_STARTL 0x07, 0x13 +#define TM6010_REQ07_R14_AGC_GATE_WIDTH 0x07, 0x14 +#define TM6010_REQ07_R15_AGC_BP_DELAY 0x07, 0x15 +#define TM6010_REQ07_R16_LOCK_COUNT 0x07, 0x16 +#define TM6010_REQ07_R17_HLOOP_MAXSTATE 0x07, 0x17 +#define TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3 0x07, 0x18 +#define TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2 0x07, 0x19 +#define TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1 0x07, 0x1a +#define TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0 0x07, 0x1b +#define TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3 0x07, 0x1c +#define TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2 0x07, 0x1d +#define TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1 0x07, 0x1e +#define TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0 0x07, 0x1f +#define TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME 0x07, 0x20 +#define TM6010_REQ07_R21_HSYNC_PHASE_OFFSET 0x07, 0x21 +#define TM6010_REQ07_R22_HSYNC_PLL_START_TIME 0x07, 0x22 +#define TM6010_REQ07_R23_HSYNC_PLL_END_TIME 0x07, 0x23 +#define TM6010_REQ07_R24_HSYNC_TIP_START_TIME 0x07, 0x24 +#define TM6010_REQ07_R25_HSYNC_TIP_END_TIME 0x07, 0x25 +#define TM6010_REQ07_R26_HSYNC_RISING_EDGE_START 0x07, 0x26 +#define TM6010_REQ07_R27_HSYNC_RISING_EDGE_END 0x07, 0x27 +#define TM6010_REQ07_R28_BACKPORCH_START 0x07, 0x28 +#define TM6010_REQ07_R29_BACKPORCH_END 0x07, 0x29 +#define TM6010_REQ07_R2A_HSYNC_FILTER_START 0x07, 0x2a +#define TM6010_REQ07_R2B_HSYNC_FILTER_END 0x07, 0x2b +#define TM6010_REQ07_R2C_CHROMA_BURST_START 0x07, 0x2c +#define TM6010_REQ07_R2D_CHROMA_BURST_END 0x07, 0x2d +#define TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART 0x07, 0x2e +#define TM6010_REQ07_R2F_ACTIVE_VIDEO_HWIDTH 0x07, 0x2f +#define TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART 0x07, 0x30 +#define TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT 0x07, 0x31 +#define TM6010_REQ07_R32_VSYNC_HLOCK_MIN 0x07, 0x32 +#define TM6010_REQ07_R33_VSYNC_HLOCK_MAX 0x07, 0x33 +#define TM6010_REQ07_R34_VSYNC_AGC_MIN 0x07, 0x34 +#define TM6010_REQ07_R35_VSYNC_AGC_MAX 0x07, 0x35 +#define TM6010_REQ07_R36_VSYNC_VBI_MIN 0x07, 0x36 +#define TM6010_REQ07_R37_VSYNC_VBI_MAX 0x07, 0x37 +#define TM6010_REQ07_R38_VSYNC_THRESHOLD 0x07, 0x38 +#define TM6010_REQ07_R39_VSYNC_TIME_CONSTANT 0x07, 0x39 +#define TM6010_REQ07_R3A_STATUS1 0x07, 0x3a +#define TM6010_REQ07_R3B_STATUS2 0x07, 0x3b +#define TM6010_REQ07_R3C_STATUS3 0x07, 0x3c +#define TM6010_REQ07_R3F_RESET 0x07, 0x3f +#define TM6010_REQ07_R40_TELETEXT_VBI_CODE0 0x07, 0x40 +#define TM6010_REQ07_R41_TELETEXT_VBI_CODE1 0x07, 0x41 +#define TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL 0x07, 0x42 +#define TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7 0x07, 0x43 +#define TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8 0x07, 0x44 +#define TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9 0x07, 0x45 +#define TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10 0x07, 0x46 +#define TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11 0x07, 0x47 +#define TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12 0x07, 0x48 +#define TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13 0x07, 0x49 +#define TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14 0x07, 0x4a +#define TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15 0x07, 0x4b +#define TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16 0x07, 0x4c +#define TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17 0x07, 0x4d +#define TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18 0x07, 0x4e +#define TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19 0x07, 0x4f +#define TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20 0x07, 0x50 +#define TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21 0x07, 0x51 +#define TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22 0x07, 0x52 +#define TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23 0x07, 0x53 +#define TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES 0x07, 0x54 +#define TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN 0x07, 0x55 +#define TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN 0x07, 0x56 +#define TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN 0x07, 0x57 +#define TM6010_REQ07_R58_VBI_CAPTION_DTO1 0x07, 0x58 +#define TM6010_REQ07_R59_VBI_CAPTION_DTO0 0x07, 0x59 +#define TM6010_REQ07_R5A_VBI_TELETEXT_DTO1 0x07, 0x5a +#define TM6010_REQ07_R5B_VBI_TELETEXT_DTO0 0x07, 0x5b +#define TM6010_REQ07_R5C_VBI_WSS625_DTO1 0x07, 0x5c +#define TM6010_REQ07_R5D_VBI_WSS625_DTO0 0x07, 0x5d +#define TM6010_REQ07_R5E_VBI_CAPTION_FRAME_START 0x07, 0x5e +#define TM6010_REQ07_R5F_VBI_WSS625_FRAME_START 0x07, 0x5f +#define TM6010_REQ07_R60_TELETEXT_FRAME_START 0x07, 0x60 +#define TM6010_REQ07_R61_VBI_CCDATA1 0x07, 0x61 +#define TM6010_REQ07_R62_VBI_CCDATA2 0x07, 0x62 +#define TM6010_REQ07_R63_VBI_WSS625_DATA1 0x07, 0x63 +#define TM6010_REQ07_R64_VBI_WSS625_DATA2 0x07, 0x64 +#define TM6010_REQ07_R65_VBI_DATA_STATUS 0x07, 0x65 +#define TM6010_REQ07_R66_VBI_CAPTION_START 0x07, 0x66 +#define TM6010_REQ07_R67_VBI_WSS625_START 0x07, 0x67 +#define TM6010_REQ07_R68_VBI_TELETEXT_START 0x07, 0x68 +#define TM6010_REQ07_R70_HSYNC_DTO_INC_STATUS3 0x07, 0x70 +#define TM6010_REQ07_R71_HSYNC_DTO_INC_STATUS2 0x07, 0x71 +#define TM6010_REQ07_R72_HSYNC_DTO_INC_STATUS1 0x07, 0x72 +#define TM6010_REQ07_R73_HSYNC_DTO_INC_STATUS0 0x07, 0x73 +#define TM6010_REQ07_R74_CHROMA_DTO_INC_STATUS3 0x07, 0x74 +#define TM6010_REQ07_R75_CHROMA_DTO_INC_STATUS2 0x07, 0x75 +#define TM6010_REQ07_R76_CHROMA_DTO_INC_STATUS1 0x07, 0x76 +#define TM6010_REQ07_R77_CHROMA_DTO_INC_STATUS0 0x07, 0x77 +#define TM6010_REQ07_R78_AGC_AGAIN_STATUS 0x07, 0x78 +#define TM6010_REQ07_R79_AGC_DGAIN_STATUS 0x07, 0x79 +#define TM6010_REQ07_R7A_CHROMA_MAG_STATUS 0x07, 0x7a +#define TM6010_REQ07_R7B_CHROMA_GAIN_STATUS1 0x07, 0x7b +#define TM6010_REQ07_R7C_CHROMA_GAIN_STATUS0 0x07, 0x7c +#define TM6010_REQ07_R7D_CORDIC_FREQ_STATUS 0x07, 0x7d +#define TM6010_REQ07_R7F_STATUS_NOISE 0x07, 0x7f +#define TM6010_REQ07_R80_COMB_FILTER_TRESHOLD 0x07, 0x80 +#define TM6010_REQ07_R82_COMB_FILTER_CONFIG 0x07, 0x82 +#define TM6010_REQ07_R83_CHROMA_LOCK_CONFIG 0x07, 0x83 +#define TM6010_REQ07_R84_NOISE_NTSC_C 0x07, 0x84 +#define TM6010_REQ07_R85_NOISE_PAL_C 0x07, 0x85 +#define TM6010_REQ07_R86_NOISE_PHASE_C 0x07, 0x86 +#define TM6010_REQ07_R87_NOISE_PHASE_Y 0x07, 0x87 +#define TM6010_REQ07_R8A_CHROMA_LOOPFILTER_STATE 0x07, 0x8a +#define TM6010_REQ07_R8B_CHROMA_HRESAMPLER 0x07, 0x8b +#define TM6010_REQ07_R8D_CPUMP_DELAY_ADJ 0x07, 0x8d +#define TM6010_REQ07_R8E_CPUMP_ADJ 0x07, 0x8e +#define TM6010_REQ07_R8F_CPUMP_DELAY 0x07, 0x8f + +/* Define TM6000/TM6010 Miscellaneous registers */ +#define TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE 0x07, 0xc0 +#define TM6010_REQ07_RC1_TRESHOLD 0x07, 0xc1 +#define TM6010_REQ07_RC2_HSYNC_WIDTH 0x07, 0xc2 +#define TM6010_REQ07_RC3_HSTART1 0x07, 0xc3 +#define TM6010_REQ07_RC4_HSTART0 0x07, 0xc4 +#define TM6010_REQ07_RC5_HEND1 0x07, 0xc5 +#define TM6010_REQ07_RC6_HEND0 0x07, 0xc6 +#define TM6010_REQ07_RC7_VSTART1 0x07, 0xc7 +#define TM6010_REQ07_RC8_VSTART0 0x07, 0xc8 +#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 +#define TM6010_REQ07_RCA_VEND0 0x07, 0xca +#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb +#define TM6010_REQ07_RCC_ACTIVE_VIDEO_IF 0x07, 0xcc +#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 +#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 +#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2 +#define TM6010_REQ07_RD3_ADDR_FOR_REQ3 0x07, 0xd3 +#define TM6010_REQ07_RD4_ADDR_FOR_REQ4 0x07, 0xd4 +#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 +#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 +#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 +#define TM6010_REQ07_RD8_IR 0x07, 0xd8 +#define TM6010_REQ07_RD8_IR_BSIZE 0x07, 0xd9 +#define TM6010_REQ07_RD8_IR_WAKEUP_SEL 0x07, 0xda +#define TM6010_REQ07_RD8_IR_WAKEUP_ADD 0x07, 0xdb +#define TM6010_REQ07_RD8_IR_LEADER1 0x07, 0xdc +#define TM6010_REQ07_RD8_IR_LEADER0 0x07, 0xdd +#define TM6010_REQ07_RD8_IR_PULSE_CNT1 0x07, 0xde +#define TM6010_REQ07_RD8_IR_PULSE_CNT0 0x07, 0xdf +#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 +#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 +#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 +#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 +#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 +#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 +#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 +#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 +#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 +#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea +#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 +#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 +#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 +#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 +#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 +#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 +#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 +#define TM6010_REQ07_RF7_BIST 0x07, 0xf7 +#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe +#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff + +/* Define TM6000/TM6010 USB registers */ +#define TM6010_REQ05_R00_MAIN_CTRL 0x05, 0x00 +#define TM6010_REQ05_R01_DEVADDR 0x05, 0x01 +#define TM6010_REQ05_R02_TEST 0x05, 0x02 +#define TM6010_REQ05_R04_SOFN0 0x05, 0x04 +#define TM6010_REQ05_R05_SOFN1 0x05, 0x05 +#define TM6010_REQ05_R06_SOFTM0 0x05, 0x06 +#define TM6010_REQ05_R07_SOFTM1 0x05, 0x07 +#define TM6010_REQ05_R08_PHY_TEST 0x05, 0x08 +#define TM6010_REQ05_R09_VCTL 0x05, 0x09 +#define TM6010_REQ05_R0A_VSTA 0x05, 0x0a +#define TM6010_REQ05_R0B_CX_CFG 0x05, 0x0b +#define TM6010_REQ05_R0C_ENDP0_REG0 0x05, 0x0c +#define TM6010_REQ05_R10_GMASK 0x05, 0x10 +#define TM6010_REQ05_R11_IMASK0 0x05, 0x11 +#define TM6010_REQ05_R12_IMASK1 0x05, 0x12 +#define TM6010_REQ05_R13_IMASK2 0x05, 0x13 +#define TM6010_REQ05_R14_IMASK3 0x05, 0x14 +#define TM6010_REQ05_R15_IMASK4 0x05, 0x15 +#define TM6010_REQ05_R16_IMASK5 0x05, 0x16 +#define TM6010_REQ05_R17_IMASK6 0x05, 0x17 +#define TM6010_REQ05_R18_IMASK7 0x05, 0x18 +#define TM6010_REQ05_R19_ZEROP0 0x05, 0x19 +#define TM6010_REQ05_R1A_ZEROP1 0x05, 0x1a +#define TM6010_REQ05_R1C_FIFO_EMP0 0x05, 0x1c +#define TM6010_REQ05_R1D_FIFO_EMP1 0x05, 0x1d +#define TM6010_REQ05_R20_IRQ_GROUP 0x05, 0x20 +#define TM6010_REQ05_R21_IRQ_SOURCE0 0x05, 0x21 +#define TM6010_REQ05_R22_IRQ_SOURCE1 0x05, 0x22 +#define TM6010_REQ05_R23_IRQ_SOURCE2 0x05, 0x23 +#define TM6010_REQ05_R24_IRQ_SOURCE3 0x05, 0x24 +#define TM6010_REQ05_R25_IRQ_SOURCE4 0x05, 0x25 +#define TM6010_REQ05_R26_IRQ_SOURCE5 0x05, 0x26 +#define TM6010_REQ05_R27_IRQ_SOURCE6 0x05, 0x27 +#define TM6010_REQ05_R28_IRQ_SOURCE7 0x05, 0x28 +#define TM6010_REQ05_R29_SEQ_ERR0 0x05, 0x29 +#define TM6010_REQ05_R2A_SEQ_ERR1 0x05, 0x2a +#define TM6010_REQ05_R2B_SEQ_ABORT0 0x05, 0x2b +#define TM6010_REQ05_R2C_SEQ_ABORT1 0x05, 0x2c +#define TM6010_REQ05_R2D_TX_ZERO0 0x05, 0x2d +#define TM6010_REQ05_R2E_TX_ZERO1 0x05, 0x2e +#define TM6010_REQ05_R2F_IDLE_CNT 0x05, 0x2f +#define TM6010_REQ05_R30_FNO_P1 0x05, 0x30 +#define TM6010_REQ05_R31_FNO_P2 0x05, 0x31 +#define TM6010_REQ05_R32_FNO_P3 0x05, 0x32 +#define TM6010_REQ05_R33_FNO_P4 0x05, 0x33 +#define TM6010_REQ05_R34_FNO_P5 0x05, 0x34 +#define TM6010_REQ05_R35_FNO_P6 0x05, 0x35 +#define TM6010_REQ05_R36_FNO_P7 0x05, 0x36 +#define TM6010_REQ05_R37_FNO_P8 0x05, 0x37 +#define TM6010_REQ05_R38_FNO_P9 0x05, 0x38 +#define TM6010_REQ05_R30_FNO_P10 0x05, 0x39 +#define TM6010_REQ05_R30_FNO_P11 0x05, 0x3a +#define TM6010_REQ05_R30_FNO_P12 0x05, 0x3b +#define TM6010_REQ05_R30_FNO_P13 0x05, 0x3c +#define TM6010_REQ05_R30_FNO_P14 0x05, 0x3d +#define TM6010_REQ05_R30_FNO_P15 0x05, 0x3e +#define TM6010_REQ05_R40_IN_MAXPS_LOW1 0x05, 0x40 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH1 0x05, 0x41 +#define TM6010_REQ05_R42_IN_MAXPS_LOW2 0x05, 0x42 +#define TM6010_REQ05_R43_IN_MAXPS_HIGH2 0x05, 0x43 +#define TM6010_REQ05_R44_IN_MAXPS_LOW3 0x05, 0x44 +#define TM6010_REQ05_R45_IN_MAXPS_HIGH3 0x05, 0x45 +#define TM6010_REQ05_R46_IN_MAXPS_LOW4 0x05, 0x46 +#define TM6010_REQ05_R47_IN_MAXPS_HIGH4 0x05, 0x47 +#define TM6010_REQ05_R48_IN_MAXPS_LOW5 0x05, 0x48 +#define TM6010_REQ05_R49_IN_MAXPS_HIGH5 0x05, 0x49 +#define TM6010_REQ05_R4A_IN_MAXPS_LOW6 0x05, 0x4a +#define TM6010_REQ05_R4B_IN_MAXPS_HIGH6 0x05, 0x4b +#define TM6010_REQ05_R4C_IN_MAXPS_LOW7 0x05, 0x4c +#define TM6010_REQ05_R4D_IN_MAXPS_HIGH7 0x05, 0x4d +#define TM6010_REQ05_R4E_IN_MAXPS_LOW8 0x05, 0x4e +#define TM6010_REQ05_R4F_IN_MAXPS_HIGH8 0x05, 0x4f +#define TM6010_REQ05_R50_IN_MAXPS_LOW9 0x05, 0x50 +#define TM6010_REQ05_R51_IN_MAXPS_HIGH9 0x05, 0x51 +#define TM6010_REQ05_R40_IN_MAXPS_LOW10 0x05, 0x52 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH10 0x05, 0x53 +#define TM6010_REQ05_R40_IN_MAXPS_LOW11 0x05, 0x54 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH11 0x05, 0x55 +#define TM6010_REQ05_R40_IN_MAXPS_LOW12 0x05, 0x56 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH12 0x05, 0x57 +#define TM6010_REQ05_R40_IN_MAXPS_LOW13 0x05, 0x58 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH13 0x05, 0x59 +#define TM6010_REQ05_R40_IN_MAXPS_LOW14 0x05, 0x5a +#define TM6010_REQ05_R41_IN_MAXPS_HIGH14 0x05, 0x5b +#define TM6010_REQ05_R40_IN_MAXPS_LOW15 0x05, 0x5c +#define TM6010_REQ05_R41_IN_MAXPS_HIGH15 0x05, 0x5d +#define TM6010_REQ05_R60_OUT_MAXPS_LOW1 0x05, 0x60 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH1 0x05, 0x61 +#define TM6010_REQ05_R62_OUT_MAXPS_LOW2 0x05, 0x62 +#define TM6010_REQ05_R63_OUT_MAXPS_HIGH2 0x05, 0x63 +#define TM6010_REQ05_R64_OUT_MAXPS_LOW3 0x05, 0x64 +#define TM6010_REQ05_R65_OUT_MAXPS_HIGH3 0x05, 0x65 +#define TM6010_REQ05_R66_OUT_MAXPS_LOW4 0x05, 0x66 +#define TM6010_REQ05_R67_OUT_MAXPS_HIGH4 0x05, 0x67 +#define TM6010_REQ05_R68_OUT_MAXPS_LOW5 0x05, 0x68 +#define TM6010_REQ05_R69_OUT_MAXPS_HIGH5 0x05, 0x69 +#define TM6010_REQ05_R6A_OUT_MAXPS_LOW6 0x05, 0x6a +#define TM6010_REQ05_R6B_OUT_MAXPS_HIGH6 0x05, 0x6b +#define TM6010_REQ05_R6C_OUT_MAXPS_LOW7 0x05, 0x6c +#define TM6010_REQ05_R6D_OUT_MAXPS_HIGH7 0x05, 0x6d +#define TM6010_REQ05_R6E_OUT_MAXPS_LOW8 0x05, 0x6e +#define TM6010_REQ05_R6F_OUT_MAXPS_HIGH8 0x05, 0x6f +#define TM6010_REQ05_R70_OUT_MAXPS_LOW9 0x05, 0x70 +#define TM6010_REQ05_R71_OUT_MAXPS_HIGH9 0x05, 0x71 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW10 0x05, 0x72 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH10 0x05, 0x73 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW11 0x05, 0x74 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH11 0x05, 0x75 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW12 0x05, 0x76 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH12 0x05, 0x77 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW13 0x05, 0x78 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH13 0x05, 0x79 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW14 0x05, 0x7a +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH14 0x05, 0x7b +#define TM6010_REQ05_R60_OUT_MAXPS_LOW15 0x05, 0x7c +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH15 0x05, 0x7d +#define TM6010_REQ05_R80_FIFO0 0x05, 0x80 +#define TM6010_REQ05_R81_FIFO1 0x05, 0x81 +#define TM6010_REQ05_R82_FIFO2 0x05, 0x82 +#define TM6010_REQ05_R83_FIFO3 0x05, 0x83 +#define TM6010_REQ05_R84_FIFO4 0x05, 0x84 +#define TM6010_REQ05_R85_FIFO5 0x05, 0x85 +#define TM6010_REQ05_R86_FIFO6 0x05, 0x86 +#define TM6010_REQ05_R87_FIFO7 0x05, 0x87 +#define TM6010_REQ05_R88_FIFO8 0x05, 0x88 +#define TM6010_REQ05_R89_FIFO9 0x05, 0x89 +#define TM6010_REQ05_R81_FIFO10 0x05, 0x8a +#define TM6010_REQ05_R81_FIFO11 0x05, 0x8b +#define TM6010_REQ05_R81_FIFO12 0x05, 0x8c +#define TM6010_REQ05_R81_FIFO13 0x05, 0x8d +#define TM6010_REQ05_R81_FIFO14 0x05, 0x8e +#define TM6010_REQ05_R81_FIFO15 0x05, 0x8f +#define TM6010_REQ05_R90_CFG_FIFO0 0x05, 0x90 +#define TM6010_REQ05_R91_CFG_FIFO1 0x05, 0x91 +#define TM6010_REQ05_R92_CFG_FIFO2 0x05, 0x92 +#define TM6010_REQ05_R93_CFG_FIFO3 0x05, 0x93 +#define TM6010_REQ05_R94_CFG_FIFO4 0x05, 0x94 +#define TM6010_REQ05_R95_CFG_FIFO5 0x05, 0x95 +#define TM6010_REQ05_R96_CFG_FIFO6 0x05, 0x96 +#define TM6010_REQ05_R97_CFG_FIFO7 0x05, 0x97 +#define TM6010_REQ05_R98_CFG_FIFO8 0x05, 0x98 +#define TM6010_REQ05_R99_CFG_FIFO9 0x05, 0x99 +#define TM6010_REQ05_R91_CFG_FIFO10 0x05, 0x9a +#define TM6010_REQ05_R91_CFG_FIFO11 0x05, 0x9b +#define TM6010_REQ05_R91_CFG_FIFO12 0x05, 0x9c +#define TM6010_REQ05_R91_CFG_FIFO13 0x05, 0x9d +#define TM6010_REQ05_R91_CFG_FIFO14 0x05, 0x9e +#define TM6010_REQ05_R91_CFG_FIFO15 0x05, 0x9f +#define TM6010_REQ05_RA0_CTL_FIFO0 0x05, 0xa0 +#define TM6010_REQ05_RA1_CTL_FIFO1 0x05, 0xa1 +#define TM6010_REQ05_RA2_CTL_FIFO2 0x05, 0xa2 +#define TM6010_REQ05_RA3_CTL_FIFO3 0x05, 0xa3 +#define TM6010_REQ05_RA4_CTL_FIFO4 0x05, 0xa4 +#define TM6010_REQ05_RA5_CTL_FIFO5 0x05, 0xa5 +#define TM6010_REQ05_RA6_CTL_FIFO6 0x05, 0xa6 +#define TM6010_REQ05_RA7_CTL_FIFO7 0x05, 0xa7 +#define TM6010_REQ05_RA8_CTL_FIFO8 0x05, 0xa8 +#define TM6010_REQ05_RA9_CTL_FIFO9 0x05, 0xa9 +#define TM6010_REQ05_RA1_CTL_FIFO10 0x05, 0xaa +#define TM6010_REQ05_RA1_CTL_FIFO11 0x05, 0xab +#define TM6010_REQ05_RA1_CTL_FIFO12 0x05, 0xac +#define TM6010_REQ05_RA1_CTL_FIFO13 0x05, 0xad +#define TM6010_REQ05_RA1_CTL_FIFO14 0x05, 0xae +#define TM6010_REQ05_RA1_CTL_FIFO15 0x05, 0xaf +#define TM6010_REQ05_RB0_BC_LOW_FIFO0 0x05, 0xb0 +#define TM6010_REQ05_RB1_BC_LOW_FIFO1 0x05, 0xb1 +#define TM6010_REQ05_RB2_BC_LOW_FIFO2 0x05, 0xb2 +#define TM6010_REQ05_RB3_BC_LOW_FIFO3 0x05, 0xb3 +#define TM6010_REQ05_RB4_BC_LOW_FIFO4 0x05, 0xb4 +#define TM6010_REQ05_RB5_BC_LOW_FIFO5 0x05, 0xb5 +#define TM6010_REQ05_RB6_BC_LOW_FIFO6 0x05, 0xb6 +#define TM6010_REQ05_RB7_BC_LOW_FIFO7 0x05, 0xb7 +#define TM6010_REQ05_RB8_BC_LOW_FIFO8 0x05, 0xb8 +#define TM6010_REQ05_RB9_BC_LOW_FIFO9 0x05, 0xb9 +#define TM6010_REQ05_RB1_BC_LOW_FIFO10 0x05, 0xba +#define TM6010_REQ05_RB1_BC_LOW_FIFO11 0x05, 0xbb +#define TM6010_REQ05_RB1_BC_LOW_FIFO12 0x05, 0xbc +#define TM6010_REQ05_RB1_BC_LOW_FIFO13 0x05, 0xbd +#define TM6010_REQ05_RB1_BC_LOW_FIFO14 0x05, 0xbe +#define TM6010_REQ05_RB1_BC_LOW_FIFO15 0x05, 0xbf +#define TM6010_REQ05_RC0_DATA_FIFO0 0x05, 0xc0 +#define TM6010_REQ05_RC4_DATA_FIFO1 0x05, 0xc4 +#define TM6010_REQ05_RC8_DATA_FIFO2 0x05, 0xc8 +#define TM6010_REQ05_RCC_DATA_FIFO3 0x05, 0xcc +#define TM6010_REQ05_RD0_DATA_FIFO4 0x05, 0xd0 +#define TM6010_REQ05_RD4_DATA_FIFO5 0x05, 0xd4 +#define TM6010_REQ05_RD8_DATA_FIFO6 0x05, 0xd8 +#define TM6010_REQ05_RDC_DATA_FIFO7 0x05, 0xdc +#define TM6010_REQ05_RE0_DATA_FIFO8 0x05, 0xe0 +#define TM6010_REQ05_RE4_DATA_FIFO9 0x05, 0xe4 +#define TM6010_REQ05_RC4_DATA_FIFO10 0x05, 0xe8 +#define TM6010_REQ05_RC4_DATA_FIFO11 0x05, 0xec +#define TM6010_REQ05_RC4_DATA_FIFO12 0x05, 0xf0 +#define TM6010_REQ05_RC4_DATA_FIFO13 0x05, 0xf4 +#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 +#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc + +/* Define TM6000/TM6010 Audio decoder registers */ +#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 +#define TM6010_REQ08_R01_A_INIT 0x08, 0x01 +#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 +#define TM6010_REQ08_R03_A_AUTO_GAIN_CTRL 0x08, 0x03 +#define TM6010_REQ08_R04_A_SIF_AMP_CTRL 0x08, 0x04 +#define TM6010_REQ08_R05_A_STANDARD_MOD 0x08, 0x05 +#define TM6010_REQ08_R06_A_SOUND_MOD 0x08, 0x06 +#define TM6010_REQ08_R07_A_LEFT_VOL 0x08, 0x07 +#define TM6010_REQ08_R08_A_RIGHT_VOL 0x08, 0x08 +#define TM6010_REQ08_R09_A_MAIN_VOL 0x08, 0x09 +#define TM6010_REQ08_R0A_A_I2S_MOD 0x08, 0x0a +#define TM6010_REQ08_R0B_A_ASD_THRES1 0x08, 0x0b +#define TM6010_REQ08_R0C_A_ASD_THRES2 0x08, 0x0c +#define TM6010_REQ08_R0D_A_AMD_THRES 0x08, 0x0d +#define TM6010_REQ08_R0E_A_MONO_THRES1 0x08, 0x0e +#define TM6010_REQ08_R0F_A_MONO_THRES2 0x08, 0x0f +#define TM6010_REQ08_R10_A_MUTE_THRES1 0x08, 0x10 +#define TM6010_REQ08_R11_A_MUTE_THRES2 0x08, 0x11 +#define TM6010_REQ08_R12_A_AGC_U 0x08, 0x12 +#define TM6010_REQ08_R13_A_AGC_ERR_T 0x08, 0x13 +#define TM6010_REQ08_R14_A_AGC_GAIN_INIT 0x08, 0x14 +#define TM6010_REQ08_R15_A_AGC_STEP_THR 0x08, 0x15 +#define TM6010_REQ08_R16_A_AGC_GAIN_MAX 0x08, 0x16 +#define TM6010_REQ08_R17_A_AGC_GAIN_MIN 0x08, 0x17 +#define TM6010_REQ08_R18_A_TR_CTRL 0x08, 0x18 +#define TM6010_REQ08_R19_A_FH_2FH_GAIN 0x08, 0x19 +#define TM6010_REQ08_R1A_A_NICAM_SER_MAX 0x08, 0x1a +#define TM6010_REQ08_R1B_A_NICAM_SER_MIN 0x08, 0x1b +#define TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT 0x08, 0x1e +#define TM6010_REQ08_R1F_A_TEST_INTF_SEL 0x08, 0x1f +#define TM6010_REQ08_R20_A_TEST_PIN_SEL 0x08, 0x20 +#define TM6010_REQ08_R21_A_AGC_ERR 0x08, 0x21 +#define TM6010_REQ08_R22_A_AGC_GAIN 0x08, 0x22 +#define TM6010_REQ08_R23_A_NICAM_INFO 0x08, 0x23 +#define TM6010_REQ08_R24_A_SER 0x08, 0x24 +#define TM6010_REQ08_R25_A_C1_AMP 0x08, 0x25 +#define TM6010_REQ08_R26_A_C2_AMP 0x08, 0x26 +#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 +#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 + +/* Define TM6000/TM6010 Video ADC registers */ +#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 +#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 +#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 +#define TM6010_REQ08_RE3_ADC_IN1_SEL 0x08, 0xe3 +#define TM6010_REQ08_RE4_ADC_IN2_SEL 0x08, 0xe4 +#define TM6010_REQ08_RE5_GAIN_PARAM 0x08, 0xe5 +#define TM6010_REQ08_RE6_POWER_DOWN_CTRL2 0x08, 0xe6 +#define TM6010_REQ08_RE7_REG_GAIN_Y 0x08, 0xe7 +#define TM6010_REQ08_RE8_REG_GAIN_C 0x08, 0xe8 +#define TM6010_REQ08_RE9_BIAS_CTRL 0x08, 0xe9 +#define TM6010_REQ08_REA_BUFF_DRV_CTRL 0x08, 0xea +#define TM6010_REQ08_REB_SIF_GAIN_CTRL 0x08, 0xeb +#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec +#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed + +/* Define TM6000/TM6010 Audio ADC registers */ +#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 +#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 +#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 +#define TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL 0x08, 0xf3 diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c new file mode 100644 index 000000000000..b3564f611e5e --- /dev/null +++ b/drivers/staging/tm6000/tm6000-stds.c @@ -0,0 +1,873 @@ +/* + tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@redhat.com> + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include "tm6000.h" +#include "tm6000-regs.h" + +struct tm6000_reg_settings { + unsigned char req; + unsigned char reg; + unsigned char value; +}; + +struct tm6000_std_tv_settings { + v4l2_std_id id; + struct tm6000_reg_settings sif[12]; + struct tm6000_reg_settings nosif[12]; + struct tm6000_reg_settings common[25]; +}; + +struct tm6000_std_settings { + v4l2_std_id id; + struct tm6000_reg_settings common[37]; +}; + +static struct tm6000_std_tv_settings tv_stds[] = { + { + .id = V4L2_STD_PAL_M, + .sif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x08}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x62}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe}, + {TM6010_REQ07_RFE_POWER_DOWN, 0xcb}, + {0, 0, 0}, + }, + .nosif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + {0, 0, 0}, + }, + .common = { + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL_Nc, + .sif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x08}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x62}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe}, + {TM6010_REQ07_RFE_POWER_DOWN, 0xcb}, + {0, 0, 0}, + }, + .nosif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + {0, 0, 0}, + }, + .common = { + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL, + .sif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x08}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x62}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe}, + {TM6010_REQ07_RFE_POWER_DOWN, 0xcb}, + {0, 0, 0} + }, + .nosif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + {0, 0, 0}, + }, + .common = { + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_SECAM, + .sif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x08}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x62}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe}, + {TM6010_REQ07_RFE_POWER_DOWN, 0xcb}, + {0, 0, 0}, + }, + .nosif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + {0, 0, 0}, + }, + .common = { + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF}, + + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_NTSC, + .sif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x08}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x62}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe}, + {TM6010_REQ07_RFE_POWER_DOWN, 0xcb}, + {0, 0, 0}, + }, + .nosif = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + {0, 0, 0}, + }, + .common = { + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, +}; + +static struct tm6000_std_settings composite_stds[] = { + { + .id = V4L2_STD_PAL_M, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL_Nc, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_SECAM, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF}, + + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_NTSC, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x0f}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe8}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8b}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, +}; + +static struct tm6000_std_settings svideo_stds[] = { + { + .id = V4L2_STD_PAL_M, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe0}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8a}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL_Nc, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe0}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8a}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe0}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8a}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x00}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_SECAM, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe0}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8a}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xFF}, + + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_NTSC, + .common = { + {TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0}, + {TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc}, + {TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8}, + {TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00}, + {TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2}, + {TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0}, + {TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2}, + {TM6010_REQ08_RED_GAIN_SEL, 0xe0}, + {TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x68}, + {TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc}, + {TM6010_REQ07_RFE_POWER_DOWN, 0x8a}, + + {TM6010_REQ07_R3F_RESET, 0x01}, + {TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01}, + {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f}, + {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, + {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x00}, + {TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b}, + {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, + {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b}, + {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2}, + {TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9}, + {TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c}, + {TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc}, + {TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc}, + {TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd}, + {TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88}, + {TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22}, + {TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61}, + {TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c}, + {TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c}, + {TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42}, + {TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6F}, + + {TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd}, + {TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07}, + {TM6010_REQ07_R3F_RESET, 0x00}, + {0, 0, 0}, + }, + }, +}; + +void tm6000_get_std_res(struct tm6000_core *dev) +{ + /* Currently, those are the only supported resoltions */ + if (dev->norm & V4L2_STD_525_60) { + dev->height = 480; + } else { + dev->height = 576; + } + dev->width = 720; +} + +static int tm6000_load_std(struct tm6000_core *dev, + struct tm6000_reg_settings *set, int max_size) +{ + int i, rc; + + /* Load board's initialization table */ + for (i = 0; max_size; i++) { + if (!set[i].req) + return 0; + + if ((dev->dev_type != TM6010) && + (set[i].req == REQ_08_SET_GET_AVREG_BIT)) + continue; + + rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting " + "req %d, reg %d to value %d\n", + rc, set[i].req, set[i].reg, set[i].value); + return rc; + } + } + + return 0; +} + +static int tm6000_set_tv(struct tm6000_core *dev, int pos) +{ + int rc; + + /* FIXME: This code is for tm6010 - not tested yet - doesn't work with + tm5600 + */ + + /* FIXME: This is tuner-dependent */ + int nosif = 0; + + if (nosif) { + rc = tm6000_load_std(dev, tv_stds[pos].nosif, + sizeof(tv_stds[pos].nosif)); + } else { + rc = tm6000_load_std(dev, tv_stds[pos].sif, + sizeof(tv_stds[pos].sif)); + } + if (rc < 0) + return rc; + rc = tm6000_load_std(dev, tv_stds[pos].common, + sizeof(tv_stds[pos].common)); + + return rc; +} + +int tm6000_set_standard(struct tm6000_core *dev, v4l2_std_id * norm) +{ + int i, rc = 0; + + dev->norm = *norm; + tm6000_get_std_res(dev); + + switch (dev->input) { + case TM6000_INPUT_TV: + for (i = 0; i < ARRAY_SIZE(tv_stds); i++) { + if (*norm & tv_stds[i].id) { + rc = tm6000_set_tv(dev, i); + goto ret; + } + } + return -EINVAL; + case TM6000_INPUT_SVIDEO: + for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) { + if (*norm & svideo_stds[i].id) { + rc = tm6000_load_std(dev, svideo_stds[i].common, + sizeof(svideo_stds[i]. + common)); + goto ret; + } + } + return -EINVAL; + case TM6000_INPUT_COMPOSITE: + for (i = 0; i < ARRAY_SIZE(composite_stds); i++) { + if (*norm & composite_stds[i].id) { + rc = tm6000_load_std(dev, + composite_stds[i].common, + sizeof(composite_stds[i]. + common)); + goto ret; + } + } + return -EINVAL; + } + +ret: + if (rc < 0) + return rc; + + msleep(40); + + + return 0; +} diff --git a/drivers/staging/tm6000/tm6000-usb-isoc.h b/drivers/staging/tm6000/tm6000-usb-isoc.h new file mode 100644 index 000000000000..5a5049acd4ec --- /dev/null +++ b/drivers/staging/tm6000/tm6000-usb-isoc.h @@ -0,0 +1,53 @@ +/* + tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/videodev2.h> + +#define TM6000_URB_MSG_LEN 180 + +struct usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct tm6000_buffer *buf; + + /* Stores the number of received fields */ + int nfields; +}; diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c new file mode 100644 index 000000000000..f2b7fe4a3581 --- /dev/null +++ b/drivers/staging/tm6000/tm6000-video.c @@ -0,0 +1,1557 @@ +/* + tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + - Fixed module load/unload + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/random.h> +#include <linux/version.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <media/v4l2-ioctl.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> +#include <linux/highmem.h> +#include <linux/freezer.h> + +#include "tm6000-regs.h" +#include "tm6000.h" + +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ + +/* Limits minimum and default number of buffers */ +#define TM6000_MIN_BUF 4 +#define TM6000_DEF_BUF 8 + +#define TM6000_MAX_ISO_PACKETS 40 /* Max number of ISO packets */ + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ + +/* Debug level */ +int tm6000_debug; + +/* supported controls */ +static struct v4l2_queryctrl tm6000_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 54, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 119, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 112, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 0x1, + .default_value = 0, + .flags = 0, + } +}; + +static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; + +static struct tm6000_fmt format[] = { + { + .name = "4:2:2, packed, YVY2", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + }, { + .name = "A/V + VBI mux packet", + .fourcc = V4L2_PIX_FMT_TM6000, + .depth = 16, + } +}; + +/* ------------------------------------------------------------------ + DMA and thread functions + ------------------------------------------------------------------*/ + +#define norm_maxw(a) 720 +#define norm_maxh(a) 576 + +#define norm_minw(a) norm_maxw(a) +#define norm_minh(a) norm_maxh(a) + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer **buf) +{ + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + char *outp; + + if (list_empty(&dma_q->active)) { + dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); + *buf = NULL; + return; + } + + *buf = list_entry(dma_q->active.next, + struct tm6000_buffer, vb.queue); + + if (!buf) + return; + + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); +// if (outp) +// memset(outp, 0, (*buf)->vb.size); + + return; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct tm6000_core *dev, + struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer *buf) +{ + /* Advice that buffer was filled */ + dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +const char *tm6000_msg_type[] = { + "unknown(0)", /* 0 */ + "video", /* 1 */ + "audio", /* 2 */ + "vbi", /* 3 */ + "pts", /* 4 */ + "err", /* 5 */ + "unknown(6)", /* 6 */ + "unknown(7)", /* 7 */ +}; + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_packet(struct urb *urb, u32 header, u8 **ptr, u8 *endp, + u8 *out_p, struct tm6000_buffer **buf) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + u8 c; + unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; + int rc = 0; + /* FIXME: move to tm6000-isoc */ + static int last_line = -2, start_line = -2, last_field = -2; + + /* FIXME: this is the hardcoded window size + */ + unsigned int linewidth = (*buf)->vb.width << 1; + + if (!dev->isoc_ctl.cmd) { + c = (header >> 24) & 0xff; + + /* split the header fields */ + size = (((header & 0x7e) << 1) -1) *4; + block = (header >> 7) & 0xf; + field = (header >> 11) & 0x1; + line = (header >> 12) & 0x1ff; + cmd = (header >> 21) & 0x7; + + /* Validates header fields */ + if(size > TM6000_URB_MSG_LEN) + size = TM6000_URB_MSG_LEN; + + if (cmd == TM6000_URB_MSG_VIDEO) { + if ((block+1)*TM6000_URB_MSG_LEN>linewidth) + cmd = TM6000_URB_MSG_ERR; + + /* FIXME: Mounts the image as field0+field1 + * It should, instead, check if the user selected + * entrelaced or non-entrelaced mode + */ + pos = ((line << 1) - field - 1) * linewidth + + block * TM6000_URB_MSG_LEN; + + /* Don't allow to write out of the buffer */ + if (pos+TM6000_URB_MSG_LEN > (*buf)->vb.size) { + dprintk(dev, V4L2_DEBUG_ISOC, + "ERR: size=%d, num=%d, line=%d, " + "field=%d\n", + size, block, line, field); + + cmd = TM6000_URB_MSG_ERR; + } + } else { + pos=0; + } + + /* Prints debug info */ + dprintk(dev, V4L2_DEBUG_ISOC, "size=%d, num=%d, " + " line=%d, field=%d\n", + size, block, line, field); + + if ((last_line!=line)&&(last_line+1!=line) && + (cmd != TM6000_URB_MSG_ERR) ) { + if (cmd != TM6000_URB_MSG_VIDEO) { + dprintk(dev, V4L2_DEBUG_ISOC, "cmd=%d, " + "size=%d, num=%d, line=%d, field=%d\n", + cmd, size, block, line, field); + } + if (start_line<0) + start_line=last_line; + /* Prints debug info */ + dprintk(dev, V4L2_DEBUG_ISOC, "lines= %d-%d, " + "field=%d\n", + start_line, last_line, field); + + if ((start_line<6 && last_line>200) && + (last_field != field) ) { + + dev->isoc_ctl.nfields++; + if (dev->isoc_ctl.nfields>=2) { + dev->isoc_ctl.nfields=0; + + /* Announces that a new buffer were filled */ + buffer_filled (dev, dma_q, *buf); + dprintk(dev, V4L2_DEBUG_ISOC, + "new buffer filled\n"); + get_next_buf (dma_q, buf); + if (!*buf) + return rc; + out_p = videobuf_to_vmalloc(&((*buf)->vb)); + if (!out_p) + return rc; + + pos = dev->isoc_ctl.pos = 0; + } + } + + start_line=line; + last_field=field; + } + if (cmd == TM6000_URB_MSG_VIDEO) + last_line = line; + + pktsize = TM6000_URB_MSG_LEN; + } else { + /* Continue the last copy */ + cmd = dev->isoc_ctl.cmd; + size= dev->isoc_ctl.size; + pos = dev->isoc_ctl.pos; + pktsize = dev->isoc_ctl.pktsize; + } + + cpysize = (endp-(*ptr) > size) ? size : endp - *ptr; + + if (cpysize) { + /* handles each different URB message */ + switch(cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + memcpy(&out_p[pos], *ptr, cpysize); + break; + case TM6000_URB_MSG_PTS: + break; + case TM6000_URB_MSG_AUDIO: +/* Need some code to process audio */ +printk ("%ld: cmd=%s, size=%d\n", jiffies, + tm6000_msg_type[cmd],size); + break; + default: + dprintk (dev, V4L2_DEBUG_ISOC, "cmd=%s, size=%d\n", + tm6000_msg_type[cmd],size); + } + } + if (cpysize<size) { + /* End of URB packet, but cmd processing is not + * complete. Preserve the state for a next packet + */ + dev->isoc_ctl.pos = pos+cpysize; + dev->isoc_ctl.size= size-cpysize; + dev->isoc_ctl.cmd = cmd; + dev->isoc_ctl.pktsize = pktsize-cpysize; + (*ptr)+=cpysize; + } else { + dev->isoc_ctl.cmd = 0; + (*ptr)+=pktsize; + } + + return rc; +} + +static int copy_streams(u8 *data, u8 *out_p, unsigned long len, + struct urb *urb, struct tm6000_buffer **buf) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); + u8 *ptr=data, *endp=data+len; + unsigned long header=0; + int rc=0; + + for (ptr=data; ptr<endp;) { + if (!dev->isoc_ctl.cmd) { + u8 *p=(u8 *)&dev->isoc_ctl.tmp_buf; + /* FIXME: This seems very complex + * It just recovers up to 3 bytes of the header that + * might be at the previous packet + */ + if (dev->isoc_ctl.tmp_buf_len) { + while (dev->isoc_ctl.tmp_buf_len) { + if ( *(ptr+3-dev->isoc_ctl.tmp_buf_len) == 0x47) { + break; + } + p++; + dev->isoc_ctl.tmp_buf_len--; + } + if (dev->isoc_ctl.tmp_buf_len) { + memcpy(&header, p, + dev->isoc_ctl.tmp_buf_len); + memcpy((u8 *)&header + + dev->isoc_ctl.tmp_buf_len, + ptr, + 4 - dev->isoc_ctl.tmp_buf_len); + ptr += 4 - dev->isoc_ctl.tmp_buf_len; + goto HEADER; + } + } + /* Seek for sync */ + for (;ptr<endp-3;ptr++) { + if (*(ptr+3)==0x47) + break; + } + + if (ptr+3>=endp) { + dev->isoc_ctl.tmp_buf_len=endp-ptr; + memcpy (&dev->isoc_ctl.tmp_buf,ptr, + dev->isoc_ctl.tmp_buf_len); + dev->isoc_ctl.cmd=0; + return rc; + } + + /* Get message header */ + header=*(unsigned long *)ptr; + ptr+=4; + } +HEADER: + /* Copy or continue last copy */ + rc=copy_packet(urb,header,&ptr,endp,out_p,buf); + if (rc<0) { + buf=NULL; + printk(KERN_ERR "tm6000: buffer underrun at %ld\n", + jiffies); + return rc; + } + if (!*buf) + return 0; + } + + return 0; +} +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_multiplexed(u8 *ptr, u8 *out_p, unsigned long len, + struct urb *urb, struct tm6000_buffer **buf) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); + unsigned int pos=dev->isoc_ctl.pos,cpysize; + int rc=1; + + while (len>0) { + cpysize=min(len,(*buf)->vb.size-pos); +//printk("Copying %d bytes (max=%lu) from %p to %p[%u]\n",cpysize,(*buf)->vb.size,ptr,out_p,pos); + memcpy(&out_p[pos], ptr, cpysize); + pos+=cpysize; + ptr+=cpysize; + len-=cpysize; + if (pos >= (*buf)->vb.size) { + pos=0; + /* Announces that a new buffer were filled */ + buffer_filled (dev, dma_q, *buf); + dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); + get_next_buf (dma_q, buf); + if (!*buf) + break; + out_p = videobuf_to_vmalloc(&((*buf)->vb)); + if (!out_p) + return rc; + pos = 0; + } + } + + dev->isoc_ctl.pos=pos; + return rc; +} + +static void inline print_err_status (struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch(status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet<0) { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + + +/* + * Controls the isoc copy of each urb packet + */ +static inline int tm6000_isoc_copy(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); + struct tm6000_buffer *buf; + int i, len=0, rc=1; + int size; + char *outp = NULL, *p; + unsigned long copied; + + get_next_buf(dma_q, &buf); + if (buf) + outp = videobuf_to_vmalloc(&buf->vb); + + if (!outp) + return 0; + + size = buf->vb.size; + + copied=0; + + if (urb->status<0) { + print_err_status (dev,-1,urb->status); + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status<0) { + print_err_status (dev,i,status); + continue; + } + + len=urb->iso_frame_desc[i].actual_length; + +// if (len>=TM6000_URB_MSG_LEN) { + p=urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (!urb->iso_frame_desc[i].status) { + if ((buf->fmt->fourcc)==V4L2_PIX_FMT_TM6000) { + rc=copy_multiplexed(p, outp, len, urb, &buf); + if (rc<=0) + return rc; + } else { + copy_streams(p, outp, len, urb, &buf); + } + } + copied += len; + if (copied >= size || !buf) + break; +// } + } + return rc; +} + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +static void tm6000_irq_callback(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + int i; + + if (!dev) + return; + + spin_lock(&dev->slock); + tm6000_isoc_copy(urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) + tm6000_err("urb resubmit failed (error=%i)\n", + urb->status); +} + +/* + * Stop and Deallocate URBs + */ +static void tm6000_uninit_isoc(struct tm6000_core *dev) +{ + struct urb *urb; + int i; + + dev->isoc_ctl.nfields = -1; + dev->isoc_ctl.buf = NULL; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb=dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_free_coherent(dev->udev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree (dev->isoc_ctl.urb); + kfree (dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb=NULL; + dev->isoc_ctl.transfer_buffer=NULL; + dev->isoc_ctl.num_bufs = 0; + + dev->isoc_ctl.num_bufs=0; +} + +/* + * Allocate URBs and start IRQ + */ +static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i, j, sb_size, pipe, size, max_packets, num_bufs = 5; + struct urb *urb; + + /* De-allocates all pending stuff */ + tm6000_uninit_isoc(dev); + + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, + dev->isoc_in.bAlternateSetting); + + pipe = usb_rcvisocpipe(dev->udev, + dev->isoc_in.endp->desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + + if (size > dev->isoc_in.maxsize) + size = dev->isoc_in.maxsize; + + dev->isoc_ctl.max_pkt_size = size; + + max_packets = ( framesize + size - 1) / size; + + if (max_packets > TM6000_MAX_ISO_PACKETS) + max_packets = TM6000_MAX_ISO_PACKETS; + + sb_size = max_packets * size; + + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + tm6000_err("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + tm6000_err("cannot allocate memory for usbtransfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets" + " (%d bytes) of %d bytes each to handle %u size\n", + max_packets, num_bufs, sb_size, + dev->isoc_in.maxsize, size); + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + tm6000_err("cannot alloc isoc_ctl.urb %i\n", i); + tm6000_uninit_isoc(dev); + usb_free_urb(urb); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + tm6000_err ("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt()?" while in int":""); + tm6000_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + tm6000_irq_callback, dma_q); + urb->interval = dev->isoc_in.endp->desc.bInterval; + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = size * j; + urb->iso_frame_desc[j].length = size; + } + } + + return 0; +} + +static int tm6000_start_thread( struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i; + + dma_q->frame=0; + dma_q->ini_jiffies=jiffies; + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + tm6000_err("submit of urb %i failed (error=%i)\n", i, + rc); + tm6000_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct tm6000_fh *fh = vq->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + if (0 == *count) + *count = TM6000_DEF_BUF; + + if (*count < TM6000_MIN_BUF) { + *count=TM6000_MIN_BUF; + } + + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + unsigned long flags; + + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); + struct tm6000_core *dev = fh->dev; + int rc = 0, urb_init = 0; + + BUG_ON(NULL == fh->fmt); + + + /* FIXME: It assumes depth=2 */ + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->vb.state = VIDEOBUF_NEEDS_INIT; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + if (0 != (rc = videobuf_iolock(vq, &buf->vb, NULL))) + goto fail; + urb_init = 1; + } + + if (!dev->isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = tm6000_prepare_isoc(dev, buf->vb.size); + if (rc < 0) + goto fail; + + rc = tm6000_start_thread(dev); + if (rc < 0) + goto fail; + + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + struct tm6000_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); + + free_buffer(vq,buf); +} + +static struct videobuf_queue_ops tm6000_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + IOCTL handling + ------------------------------------------------------------------*/ + +static int res_get(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + dev->resources =1; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); + mutex_unlock(&dev->lock); + return 1; +} + +static int res_locked(struct tm6000_core *dev) +{ + return (dev->resources); +} + +static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + mutex_lock(&dev->lock); + dev->resources = 0; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); + mutex_unlock(&dev->lock); +} + +/* ------------------------------------------------------------------ + IOCTL vidioc handling + ------------------------------------------------------------------*/ +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *cap) +{ + // struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + + strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); + strlcpy(cap->card,"Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); + // strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); + cap->version = TM6000_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_TUNER | + V4L2_CAP_READWRITE; + return 0; +} + +static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(format))) + return -EINVAL; + + strlcpy(f->description,format[f->index].name,sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh=priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return (0); +} + +static struct tm6000_fmt* format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return format+i; + return NULL; +} + +static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + struct tm6000_fmt *fmt; + enum v4l2_field field; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) { + dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)" + " invalid.\n", f->fmt.pix.pixelformat); + return -EINVAL; + } + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) { +// field=V4L2_FIELD_INTERLACED; + field=V4L2_FIELD_SEQ_TB; + } else if (V4L2_FIELD_INTERLACED != field) { + dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n"); + return -EINVAL; + } + + tm6000_get_std_res (dev); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + + f->fmt.pix.width &= ~0x01; + + f->fmt.pix.field = field; + + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +/*FIXME: This seems to be generic enough to be at videodev2 */ +static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + int ret = vidioc_try_fmt_vid_cap(file,fh,f); + if (ret < 0) + return (ret); + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + + dev->fourcc = f->fmt.pix.pixelformat; + + tm6000_set_fourcc_format(dev); + + return (0); +} + +static int vidioc_reqbufs (struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct tm6000_fh *fh=priv; + + return (videobuf_reqbufs(&fh->vb_vidq, p)); +} + +static int vidioc_querybuf (struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct tm6000_fh *fh=priv; + + return (videobuf_querybuf(&fh->vb_vidq, p)); +} + +static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh=priv; + + return (videobuf_qbuf(&fh->vb_vidq, p)); +} + +static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh=priv; + + return (videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK)); +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct tm6000_fh *fh=priv; + + return videobuf_cgmbuf (&fh->vb_vidq, mbuf, 8); +} +#endif + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + if (!res_get(dev,fh)) + return -EBUSY; + return (videobuf_streamon(&fh->vb_vidq)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + videobuf_streamoff(&fh->vb_vidq); + res_free(dev,fh); + + return (0); +} + +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *norm) +{ + int rc=0; + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + + rc=tm6000_set_standard (dev, norm); + + fh->width = dev->width; + fh->height = dev->height; + + if (rc<0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + return 0; +} + +static int vidioc_enum_input (struct file *file, void *priv, + struct v4l2_input *inp) +{ + switch (inp->index) { + case TM6000_INPUT_TV: + inp->type = V4L2_INPUT_TYPE_TUNER; + strcpy(inp->name,"Television"); + break; + case TM6000_INPUT_COMPOSITE: + inp->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(inp->name,"Composite"); + break; + case TM6000_INPUT_SVIDEO: + inp->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(inp->name,"S-Video"); + break; + default: + return -EINVAL; + } + inp->std = TM6000_STD; + + return 0; +} + +static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + + *i=dev->input; + + return 0; +} +static int vidioc_s_input (struct file *file, void *priv, unsigned int i) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + int rc=0; + char buf[1]; + + switch (i) { + case TM6000_INPUT_TV: + dev->input=i; + *buf=0; + break; + case TM6000_INPUT_COMPOSITE: + case TM6000_INPUT_SVIDEO: + dev->input=i; + *buf=1; + break; + default: + return -EINVAL; + } + rc=tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR, + REQ_03_SET_GET_MCU_PIN, 0x03, 1, buf, 1); + + if (!rc) { + dev->input=i; + rc=vidioc_s_std (file, priv, &dev->vfd->current_norm); + } + + return (rc); +} + + /* --- controls ---------------------------------------------- */ +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) + if (qc->id && qc->id == tm6000_qctrl[i].id) { + memcpy(qc, &(tm6000_qctrl[i]), + sizeof(*qc)); + return (0); + } + + return -EINVAL; +} + +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + int val; + + /* FIXME: Probably, those won't work! Maybe we need shadow regs */ + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0); + break; + case V4L2_CID_BRIGHTNESS: + val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0); + return 0; + case V4L2_CID_SATURATION: + val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0); + return 0; + case V4L2_CID_HUE: + val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0); + return 0; + default: + return -EINVAL; + } + + if (val<0) + return val; + + ctrl->value=val; + + return 0; +} +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + u8 val=ctrl->value; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); + return 0; + case V4L2_CID_BRIGHTNESS: + tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); + return 0; + case V4L2_CID_SATURATION: + tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); + return 0; + case V4L2_CID_HUE: + tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); + return 0; + } + return -EINVAL; +} + +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->rxsubchans = V4L2_TUNER_SUB_MONO; + + return 0; +} + +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + return 0; +} + +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = dev->freq; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + + return 0; +} + +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (unlikely(f->tuner != 0)) + return -EINVAL; + +// mutex_lock(&dev->lock); + dev->freq = f->frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); +// mutex_unlock(&dev->lock); + + return 0; +} + +/* ------------------------------------------------------------------ + File operations for the device + ------------------------------------------------------------------*/ + +static int tm6000_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct tm6000_core *dev = video_drvdata(file); + struct tm6000_fh *fh; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int i,rc; + + printk(KERN_INFO "tm6000: open called (dev=%s)\n", + video_device_node_name(vdev)); + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", + video_device_node_name(vdev)); + + + /* If more than one user, mutex should be added */ + dev->users++; + + dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n", + video_device_node_name(vdev), v4l2_type_names[type], + dev->users); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh),GFP_KERNEL); + if (NULL == fh) { + dev->users--; + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dev->fourcc = format[0].fourcc; + + fh->fmt = format_by_fourcc(dev->fourcc); + + tm6000_get_std_res (dev); + + fh->width = dev->width; + fh->height = dev->height; + + dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, " + "dev->vidq=0x%08lx\n", + (unsigned long)fh,(unsigned long)dev,(unsigned long)&dev->vidq); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " + "queued=%d\n",list_empty(&dev->vidq.queued)); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " + "active=%d\n",list_empty(&dev->vidq.active)); + + /* initialize hardware on analog mode */ + if (dev->mode!=TM6000_MODE_ANALOG) { + rc=tm6000_init_analog_mode (dev); + if (rc<0) + return rc; + + /* Put all controls at a sane state */ + for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) + qctl_regs[i] =tm6000_qctrl[i].default_value; + + dev->mode=TM6000_MODE_ANALOG; + } + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct tm6000_buffer),fh); + + return 0; +} + +static ssize_t +tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) +{ + struct tm6000_fh *fh = file->private_data; + + if (fh->type==V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (res_locked(fh->dev)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, + file->f_flags & O_NONBLOCK); + } + return 0; +} + +static unsigned int +tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_buffer *buf; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + if (res_get(fh->dev,fh)) { + /* streaming capture */ + if (list_empty(&fh->vb_vidq.stream)) + return POLLERR; + buf = list_entry(fh->vb_vidq.stream.next,struct tm6000_buffer,vb.stream); + } else { + /* read() capture */ + return videobuf_poll_stream(file, &fh->vb_vidq, + wait); + } + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN|POLLRDNORM; + return 0; +} + +static int tm6000_release(struct file *file) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n", + video_device_node_name(vdev), dev->users); + + dev->users--; + + if (!dev->users) { + tm6000_uninit_isoc(dev); + videobuf_mmap_free(&fh->vb_vidq); + } + + kfree (fh); + + return 0; +} + +static int tm6000_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct tm6000_fh *fh = file->private_data; + int ret; + + ret=videobuf_mmap_mapper(&fh->vb_vidq, vma); + + return ret; +} + +static struct v4l2_file_operations tm6000_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .read = tm6000_read, + .poll = tm6000_poll, + .mmap = tm6000_mmap, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +}; + +static struct video_device tm6000_template = { + .name = "tm6000", + .fops = &tm6000_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release, + .tvnorms = TM6000_STD, + .current_norm = V4L2_STD_NTSC_M, +}; + +/* ----------------------------------------------------------------- + Initialization and module stuff + ------------------------------------------------------------------*/ + +int tm6000_v4l2_register(struct tm6000_core *dev) +{ + int ret = -1; + struct video_device *vfd; + + vfd = video_device_alloc(); + if(!vfd) { + return -ENOMEM; + } + dev->vfd = vfd; + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + memcpy (dev->vfd, &tm6000_template, sizeof(*(dev->vfd))); + dev->vfd->debug=tm6000_debug; + vfd->v4l2_dev = &dev->v4l2_dev; + video_set_drvdata(vfd, dev); + + ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr); + printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); + return ret; +} + +int tm6000_v4l2_unregister(struct tm6000_core *dev) +{ + video_unregister_device(dev->vfd); + + return 0; +} + +int tm6000_v4l2_exit(void) +{ + return 0; +} + +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr,"Allow changing video device number"); + +module_param_named (debug, tm6000_debug, int, 0444); +MODULE_PARM_DESC(debug,"activates debug info"); + +module_param(vid_limit,int,0644); +MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes"); + diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/staging/tm6000/tm6000.h new file mode 100644 index 000000000000..6812d6867d57 --- /dev/null +++ b/drivers/staging/tm6000/tm6000.h @@ -0,0 +1,301 @@ +/* + tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + - DVB-T support + + 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 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +// Use the tm6000-hack, instead of the proper initialization code +//#define HACK 1 + +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> +#include "tm6000-usb-isoc.h" +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <media/v4l2-device.h> + + +#include <linux/dvb/frontend.h> +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dmxdev.h" + +#define TM6000_VERSION KERNEL_VERSION(0, 0, 2) + +/* Inputs */ + +enum tm6000_itype { + TM6000_INPUT_TV = 0, + TM6000_INPUT_COMPOSITE, + TM6000_INPUT_SVIDEO, +}; + +enum tm6000_devtype { + TM6000 = 0, + TM5600, + TM6010, +}; + +/* ------------------------------------------------------------------ + Basic structures + ------------------------------------------------------------------*/ + +struct tm6000_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; +}; + +/* buffer for one video frame */ +struct tm6000_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct tm6000_fmt *fmt; +}; + +struct tm6000_dmaqueue { + struct list_head active; + struct list_head queued; + + /* thread for generating video stream*/ + struct task_struct *kthread; + wait_queue_head_t wq; + /* Counters to control fps rate */ + int frame; + int ini_jiffies; +}; + +/* device states */ +enum tm6000_core_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +/* io methods */ +enum tm6000_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum tm6000_mode { + TM6000_MODE_UNKNOWN=0, + TM6000_MODE_ANALOG, + TM6000_MODE_DIGITAL, +}; + +struct tm6000_gpio { + int tuner_reset; + int tuner_on; + int demod_reset; + int demod_on; + int power_led; + int dvb_led; + int ir; +}; + +struct tm6000_capabilities { + unsigned int has_tuner:1; + unsigned int has_tda9874:1; + unsigned int has_dvb:1; + unsigned int has_zl10353:1; + unsigned int has_eeprom:1; + unsigned int has_remote:1; +}; + +struct tm6000_dvb { + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dvb_frontend *frontend; + struct dmxdev dmxdev; + unsigned int streams; + struct urb *bulk_urb; + struct mutex mutex; +}; + +struct tm6000_endpoint { + struct usb_host_endpoint *endp; + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + unsigned maxsize; +}; + +struct tm6000_core { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + enum tm6000_devtype dev_type; /* type of device */ + + v4l2_std_id norm; /* Current norm */ + int width,height; /* Selected resolution */ + + enum tm6000_core_state state; + + /* Device Capabilities*/ + struct tm6000_capabilities caps; + + /* Tuner configuration */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + + struct tm6000_gpio gpio; + + /* Demodulator configuration */ + int demod_addr; /* demodulator address */ + + int audio_bitrate; + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + + /* video for linux */ + int users; + + /* various device info */ + unsigned int resources; + struct video_device *vfd; + struct tm6000_dmaqueue vidq; + struct v4l2_device v4l2_dev; + + int input; + int freq; + unsigned int fourcc; + + enum tm6000_mode mode; + + /* DVB-T support */ + struct tm6000_dvb *dvb; + + /* locks */ + struct mutex lock; + + /* usb transfer */ + struct usb_device *udev; /* the usb device */ + + struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out; + + /* scaler!=0 if scaler is active*/ + int scaler; + + /* Isoc control struct */ + struct usb_isoc_ctl isoc_ctl; + + spinlock_t slock; +}; + +struct tm6000_fh { + struct tm6000_core *dev; + + /* video capture */ + struct tm6000_fmt *fmt; + unsigned int width,height; + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +#define TM6000_STD V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM + +/* In tm6000-cards.c */ + +int tm6000_tuner_callback (void *ptr, int component, int command, int arg); +int tm6000_xc5000_callback (void *ptr, int component, int command, int arg); +int tm6000_cards_setup(struct tm6000_core *dev); + +/* In tm6000-core.c */ + +int tm6000_read_write_usb (struct tm6000_core *dev, u8 reqtype, u8 req, + u16 value, u16 index, u8 *buf, u16 len); +int tm6000_get_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_init (struct tm6000_core *dev); + +int tm6000_init_analog_mode (struct tm6000_core *dev); +int tm6000_init_digital_mode (struct tm6000_core *dev); +int tm6000_set_audio_bitrate (struct tm6000_core *dev, int bitrate); + +int tm6000_dvb_register(struct tm6000_core *dev); +void tm6000_dvb_unregister(struct tm6000_core *dev); + +int tm6000_v4l2_register(struct tm6000_core *dev); +int tm6000_v4l2_unregister(struct tm6000_core *dev); +int tm6000_v4l2_exit(void); +void tm6000_set_fourcc_format(struct tm6000_core *dev); + +/* In tm6000-stds.c */ +void tm6000_get_std_res(struct tm6000_core *dev); +int tm6000_set_standard (struct tm6000_core *dev, v4l2_std_id *norm); + +/* In tm6000-i2c.c */ +int tm6000_i2c_register(struct tm6000_core *dev); +int tm6000_i2c_unregister(struct tm6000_core *dev); + +/* In tm6000-queue.c */ + +int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma); + +int tm6000_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_reqbufs (struct file *file, void *priv, + struct v4l2_requestbuffers *rb); +int tm6000_vidioc_querybuf (struct file *file, void *priv, + struct v4l2_buffer *b); +int tm6000_vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *b); +int tm6000_vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *b); +ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, + loff_t * f_pos); +unsigned int tm6000_v4l2_poll(struct file *file, + struct poll_table_struct *wait); +int tm6000_queue_init(struct tm6000_core *dev); + +/* In tm6000-alsa.c */ +int tm6000_audio_init(struct tm6000_core *dev, int idx); + + +/* Debug stuff */ + +extern int tm6000_debug; + +#define dprintk(dev, level, fmt, arg...) do {\ + if (tm6000_debug & level) \ + printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ + dev->name, __FUNCTION__ , ##arg); } while (0) + +#define V4L2_DEBUG_REG 0x0004 +#define V4L2_DEBUG_I2C 0x0008 +#define V4L2_DEBUG_QUEUE 0x0010 +#define V4L2_DEBUG_ISOC 0x0020 +#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */ +#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */ + +#define tm6000_err(fmt, arg...) do {\ + printk(KERN_ERR "tm6000 %s :"fmt, \ + __FUNCTION__ , ##arg); } while (0) + + diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c index ed3070edcac1..4fcc4351e73f 100644 --- a/drivers/staging/vt6655/rxtx.c +++ b/drivers/staging/vt6655/rxtx.c @@ -25,19 +25,19 @@ * Date: May 20, 2003 * * Functions: - * s_vGenerateTxParameter - Generate tx dma requried parameter. + * s_vGenerateTxParameter - Generate tx dma required parameter. * vGenerateMACHeader - Translate 802.3 to 802.11 header - * cbGetFragCount - Caculate fragement number count + * cbGetFragCount - Caculate fragment number count * csBeacon_xmit - beacon tx function * csMgmt_xmit - management tx function * s_cbFillTxBufHead - fulfill tx dma buffer header * s_uGetDataDuration - get tx data required duration * s_uFillDataHead- fulfill tx data duration header - * s_uGetRTSCTSDuration- get rtx/cts requried duration + * s_uGetRTSCTSDuration- get rtx/cts required duration * s_uGetRTSCTSRsvTime- get rts/cts reserved time * s_uGetTxRsvTime- get frame reserved time * s_vFillCTSHead- fulfill CTS ctl header - * s_vFillFragParameter- Set fragement ctl parameter. + * s_vFillFragParameter- Set fragment ctl parameter. * s_vFillRTSHead- fulfill RTS ctl header * s_vFillTxKey- fulfill tx encrypt key * s_vSWencryption- Software encrypt header @@ -877,7 +877,7 @@ s_vFillRTSHead ( } // Note: So far RTSHead dosen't appear in ATIM & Beacom DMA, so we don't need to take them into account. - // Otherwise, we need to modified codes for them. + // Otherwise, we need to modify codes for them. if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) { if (byFBOption == AUTO_FB_NONE) { PSRTS_g pBuf = (PSRTS_g)pvRTS; @@ -1133,7 +1133,7 @@ s_vFillCTSHead ( * * Parameters: * In: - * pDevice - Pointer to adpater + * pDevice - Pointer to adapter * pTxDataHead - Transmit Data Buffer * pTxBufHead - pTxBufHead * pvRrvTime - pvRrvTime @@ -2252,7 +2252,7 @@ vGenerateFIFOHeader ( * * Parameters: * In: - * pDevice - Pointer to adpater + * pDevice - Pointer to adapter * dwTxBufferAddr - Transmit Buffer * pPacket - Packet from upper layer * cbPacketSize - Transmit Data Length diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c index d9fa36c95230..a2ce6fad8ee5 100644 --- a/drivers/staging/vt6656/rxtx.c +++ b/drivers/staging/vt6656/rxtx.c @@ -25,17 +25,17 @@ * Date: May 20, 2003 * * Functions: - * s_vGenerateTxParameter - Generate tx dma requried parameter. + * s_vGenerateTxParameter - Generate tx dma required parameter. * s_vGenerateMACHeader - Translate 802.3 to 802.11 header * csBeacon_xmit - beacon tx function * csMgmt_xmit - management tx function * s_uGetDataDuration - get tx data required duration * s_uFillDataHead- fulfill tx data duration header - * s_uGetRTSCTSDuration- get rtx/cts requried duration + * s_uGetRTSCTSDuration- get rtx/cts required duration * s_uGetRTSCTSRsvTime- get rts/cts reserved time * s_uGetTxRsvTime- get frame reserved time * s_vFillCTSHead- fulfill CTS ctl header - * s_vFillFragParameter- Set fragement ctl parameter. + * s_vFillFragParameter- Set fragment ctl parameter. * s_vFillRTSHead- fulfill RTS ctl header * s_vFillTxKey- fulfill tx encrypt key * s_vSWencryption- Software encrypt header diff --git a/drivers/staging/wavelan/wavelan_cs.c b/drivers/staging/wavelan/wavelan_cs.c index 04f691d127b4..37fa85517a58 100644 --- a/drivers/staging/wavelan/wavelan_cs.c +++ b/drivers/staging/wavelan/wavelan_cs.c @@ -3850,12 +3850,8 @@ wv_pcmcia_config(struct pcmcia_device * link) if (i != 0) break; - /* - * Now allocate an interrupt line. Note that this does not - * actually assign a handler to the interrupt. - */ - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) + i = pcmcia_request_interrupt(link, wavelan_interrupt); + if (!i) break; /* @@ -3890,7 +3886,7 @@ wv_pcmcia_config(struct pcmcia_device * link) break; /* Feed device with this info... */ - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; netif_start_queue(dev); @@ -4437,10 +4433,6 @@ wavelan_probe(struct pcmcia_device *p_dev) p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; p_dev->io.IOAddrLines = 3; - /* Interrupt setup */ - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - p_dev->irq.Handler = wavelan_interrupt; - /* General socket configuration */ p_dev->conf.Attributes = CONF_ENABLE_IRQ; p_dev->conf.IntType = INT_MEMORY_AND_IO; @@ -4487,7 +4479,6 @@ wavelan_probe(struct pcmcia_device *p_dev) ret = wv_hw_config(dev); if (ret) { - dev->irq = 0; pcmcia_disable_device(p_dev); return ret; } diff --git a/drivers/staging/wlags49_h2/wl_cs.c b/drivers/staging/wlags49_h2/wl_cs.c index 9da42e66085e..c9d99d88b786 100644 --- a/drivers/staging/wlags49_h2/wl_cs.c +++ b/drivers/staging/wlags49_h2/wl_cs.c @@ -156,15 +156,12 @@ static int wl_adapter_attach(struct pcmcia_device *link) link->io.NumPorts1 = HCF_NUM_IO_PORTS; link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; link->io.IOAddrLines = 6; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; - link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; - link->irq.Handler = &wl_isr; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 5; link->conf.Present = PRESENT_OPTION; - link->priv = link->irq.Instance = dev; + link->priv = dev; lp = wl_priv(dev); lp->link = link; @@ -318,11 +315,11 @@ void wl_adapter_insert( struct pcmcia_device *link ) link->conf.Attributes |= CONF_ENABLE_IRQ; CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io)); - CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, wl_isr)); CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); - dev->irq = link->irq.AssignedIRQ; + dev->irq = link->irq; dev->base_addr = link->io.BasePort1; SET_NETDEV_DEV(dev, &handle_to_dev(link)); diff --git a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c index d442fd35620a..99cb2246ac72 100644 --- a/drivers/telephony/ixj_pcmcia.c +++ b/drivers/telephony/ixj_pcmcia.c @@ -22,7 +22,6 @@ typedef struct ixj_info_t { int ndev; - dev_node_t node; struct ixj *port; } ixj_info_t; @@ -155,8 +154,6 @@ static int ixj_config(struct pcmcia_device * link) j = ixj_pcmcia_probe(link->io.BasePort1, link->io.BasePort1 + 0x10); info->ndev = 1; - info->node.major = PHONE_MAJOR; - link->dev_node = &info->node; ixj_get_serial(link, j); return 0; diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index df1bae9b048e..eaa79c8a9b8c 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -366,6 +366,13 @@ rescan: if (is_done) done(ep, req, 0); else if (ep->is_pingpong) { + /* + * One dummy read to delay the code because of a HW glitch: + * CSR returns bad RXCOUNT when read too soon after updating + * RX_DATA_BK flags. + */ + csr = __raw_readl(creg); + bufferspace -= count; buf += count; goto rescan; diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 40a858335035..0cd6c7795d90 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -181,7 +181,7 @@ struct ehci_hcd_omap { void __iomem *ehci_base; /* Regulators for USB PHYs. - * Each PHY can have a seperate regulator. + * Each PHY can have a separate regulator. */ struct regulator *regulator[OMAP3_HS_USB_PORTS]; }; diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 39d253e841f6..58cb73c8420a 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -47,7 +47,6 @@ static const char driver_name[DEV_NAME_LEN] = "sl811_cs"; typedef struct local_info_t { struct pcmcia_device *p_dev; - dev_node_t node; } local_info_t; static void sl811_cs_release(struct pcmcia_device * link); @@ -163,8 +162,7 @@ static int sl811_cs_config_check(struct pcmcia_device *p_dev, dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; /* we need an interrupt */ - if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; @@ -186,7 +184,6 @@ static int sl811_cs_config_check(struct pcmcia_device *p_dev, static int sl811_cs_config(struct pcmcia_device *link) { struct device *parent = &link->dev; - local_info_t *dev = link->priv; int ret; dev_dbg(&link->dev, "sl811_cs_config\n"); @@ -197,31 +194,24 @@ static int sl811_cs_config(struct pcmcia_device *link) /* require an IRQ and two registers */ if (!link->io.NumPorts1 || link->io.NumPorts1 < 2) goto failed; - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - ret = pcmcia_request_irq(link, &link->irq); - if (ret) - goto failed; - } else + + if (!link->irq) goto failed; ret = pcmcia_request_configuration(link, &link->conf); if (ret) goto failed; - sprintf(dev->node.dev_name, driver_name); - dev->node.major = dev->node.minor = 0; - link->dev_node = &dev->node; - - printk(KERN_INFO "%s: index 0x%02x: ", - dev->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x: ", + link->conf.ConfigIndex); if (link->conf.Vpp) printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10); - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); printk("\n"); - if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ) + if (sl811_hc_init(parent, link->io.BasePort1, link->irq) < 0) { failed: printk(KERN_WARNING "sl811_cs_config failed\n"); @@ -241,10 +231,6 @@ static int sl811_cs_probe(struct pcmcia_device *link) local->p_dev = link; link->priv = local; - /* Initialize */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.Handler = NULL; - link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 112ef7e26f6b..608d61a105f6 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -76,7 +76,7 @@ * xfers-per-ripe, blocks-per-rpipe, rpipes-per-host), at the end * we are going to have to rebuild all this based on an scheduler, * to where we have a list of transactions to do and based on the - * availability of the different requried components (blocks, + * availability of the different required components (blocks, * rpipes, segment slots, etc), we go scheduling them. Painful. */ #include <linux/init.h> diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index dca48df98444..e5d6b56d4447 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -50,8 +50,9 @@ #include <linux/fb.h> #include <linux/init.h> #include <linux/ioport.h> - +#include <linux/platform_device.h> #include <linux/uaccess.h> + #include <asm/system.h> #include <asm/irq.h> #include <asm/amigahw.h> @@ -1135,7 +1136,7 @@ static int amifb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg * Interface to the low level console driver */ -static void amifb_deinit(void); +static void amifb_deinit(struct platform_device *pdev); /* * Internal routines @@ -2246,7 +2247,7 @@ static inline void chipfree(void) * Initialisation */ -static int __init amifb_init(void) +static int __init amifb_probe(struct platform_device *pdev) { int tag, i, err = 0; u_long chipptr; @@ -2261,16 +2262,6 @@ static int __init amifb_init(void) } amifb_setup(option); #endif - if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_VIDEO)) - return -ENODEV; - - /* - * We request all registers starting from bplpt[0] - */ - if (!request_mem_region(CUSTOM_PHYSADDR+0xe0, 0x120, - "amifb [Denise/Lisa]")) - return -EBUSY; - custom.dmacon = DMAF_ALL | DMAF_MASTER; switch (amiga_chipset) { @@ -2377,6 +2368,7 @@ default_chipset: fb_info.fbops = &amifb_ops; fb_info.par = ¤tpar; fb_info.flags = FBINFO_DEFAULT; + fb_info.device = &pdev->dev; if (!fb_find_mode(&fb_info.var, &fb_info, mode_option, ami_modedb, NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) { @@ -2451,18 +2443,18 @@ default_chipset: return 0; amifb_error: - amifb_deinit(); + amifb_deinit(pdev); return err; } -static void amifb_deinit(void) +static void amifb_deinit(struct platform_device *pdev) { if (fb_info.cmap.len) fb_dealloc_cmap(&fb_info.cmap); + fb_dealloc_cmap(&fb_info.cmap); chipfree(); if (videomemory) iounmap((void*)videomemory); - release_mem_region(CUSTOM_PHYSADDR+0xe0, 0x120); custom.dmacon = DMAF_ALL | DMAF_MASTER; } @@ -3794,14 +3786,35 @@ static void ami_rebuild_copper(void) } } -static void __exit amifb_exit(void) +static int __exit amifb_remove(struct platform_device *pdev) { unregister_framebuffer(&fb_info); - amifb_deinit(); + amifb_deinit(pdev); amifb_video_off(); + return 0; +} + +static struct platform_driver amifb_driver = { + .remove = __exit_p(amifb_remove), + .driver = { + .name = "amiga-video", + .owner = THIS_MODULE, + }, +}; + +static int __init amifb_init(void) +{ + return platform_driver_probe(&amifb_driver, amifb_probe); } module_init(amifb_init); + +static void __exit amifb_exit(void) +{ + platform_driver_unregister(&amifb_driver); +} + module_exit(amifb_exit); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:amiga-video"); diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 8d8dfda2f868..6df7c54db0a3 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -299,6 +299,7 @@ static const struct zorro_device_id cirrusfb_zorro_table[] = { }, { 0 } }; +MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table); static const struct { zorro_id id2; diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c index 5eb61b5adfe8..42fe155aba0e 100644 --- a/drivers/video/cobalt_lcdfb.c +++ b/drivers/video/cobalt_lcdfb.c @@ -123,7 +123,7 @@ static void lcd_clear(struct fb_info *info) lcd_write_control(info, LCD_RESET); } -static struct fb_fix_screeninfo cobalt_lcdfb_fix __initdata = { +static struct fb_fix_screeninfo cobalt_lcdfb_fix __devinitdata = { .id = "cobalt-lcd", .type = FB_TYPE_TEXT, .type_aux = FB_AUX_TEXT_MDA, diff --git a/drivers/video/fm2fb.c b/drivers/video/fm2fb.c index 6c91c61cdb63..1b0feb8e7244 100644 --- a/drivers/video/fm2fb.c +++ b/drivers/video/fm2fb.c @@ -219,6 +219,7 @@ static struct zorro_device_id fm2fb_devices[] __devinitdata = { { ZORRO_PROD_HELFRICH_RAINBOW_II }, { 0 } }; +MODULE_DEVICE_TABLE(zorro, fm2fb_devices); static struct zorro_driver fm2fb_driver = { .name = "fm2fb", diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 24747aef1952..95896f387927 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -655,7 +655,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev, /* we use the subsystem vendor/device id as the virtio vendor/device * id. this allows us to use the same PCI vendor/device id for all * virtio devices and to identify the particular virtio driver by - * the subsytem ids */ + * the subsystem ids */ vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor; vp_dev->vdev.id.device = pci_dev->subsystem_device; diff --git a/drivers/vlynq/Kconfig b/drivers/vlynq/Kconfig index a9efb1625321..d874b4f34136 100644 --- a/drivers/vlynq/Kconfig +++ b/drivers/vlynq/Kconfig @@ -1,8 +1,8 @@ menu "TI VLYNQ" + depends on AR7 && EXPERIMENTAL config VLYNQ bool "TI VLYNQ bus support" - depends on AR7 && EXPERIMENTAL help Support for Texas Instruments(R) VLYNQ bus. The VLYNQ bus is a high-speed, serial and packetized diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 2ac4440e7b08..8943b8ccee1a 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -80,12 +80,6 @@ static void do_suspend(void) shutting_down = SHUTDOWN_SUSPEND; - err = stop_machine_create(); - if (err) { - printk(KERN_ERR "xen suspend: failed to setup stop_machine %d\n", err); - goto out; - } - #ifdef CONFIG_PREEMPT /* If the kernel is preemptible, we need to freeze all the processes to prevent them from being in the middle of a pagetable update @@ -93,7 +87,7 @@ static void do_suspend(void) err = freeze_processes(); if (err) { printk(KERN_ERR "xen suspend: freeze failed %d\n", err); - goto out_destroy_sm; + goto out; } #endif @@ -136,12 +130,8 @@ out_resume: out_thaw: #ifdef CONFIG_PREEMPT thaw_processes(); - -out_destroy_sm: -#endif - stop_machine_destroy(); - out: +#endif shutting_down = SHUTDOWN_INVALID; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/zorro/proc.c b/drivers/zorro/proc.c index d47c47fc048f..3c7046d79654 100644 --- a/drivers/zorro/proc.c +++ b/drivers/zorro/proc.c @@ -97,7 +97,7 @@ static void zorro_seq_stop(struct seq_file *m, void *v) static int zorro_seq_show(struct seq_file *m, void *v) { - u_int slot = *(loff_t *)v; + unsigned int slot = *(loff_t *)v; struct zorro_dev *z = &zorro_autocon[slot]; seq_printf(m, "%02x\t%08x\t%08lx\t%08lx\t%02x\n", slot, z->id, @@ -129,7 +129,7 @@ static const struct file_operations zorro_devices_proc_fops = { static struct proc_dir_entry *proc_bus_zorro_dir; -static int __init zorro_proc_attach_device(u_int slot) +static int __init zorro_proc_attach_device(unsigned int slot) { struct proc_dir_entry *entry; char name[4]; @@ -146,7 +146,7 @@ static int __init zorro_proc_attach_device(u_int slot) static int __init zorro_proc_init(void) { - u_int slot; + unsigned int slot; if (MACH_IS_AMIGA && AMIGAHW_PRESENT(ZORRO)) { proc_bus_zorro_dir = proc_mkdir("bus/zorro", NULL); diff --git a/drivers/zorro/zorro-driver.c b/drivers/zorro/zorro-driver.c index 53180a37cc9a..7ee2b6e71786 100644 --- a/drivers/zorro/zorro-driver.c +++ b/drivers/zorro/zorro-driver.c @@ -137,10 +137,34 @@ static int zorro_bus_match(struct device *dev, struct device_driver *drv) return 0; } +static int zorro_uevent(struct device *dev, struct kobj_uevent_env *env) +{ +#ifdef CONFIG_HOTPLUG + struct zorro_dev *z; + + if (!dev) + return -ENODEV; + + z = to_zorro_dev(dev); + if (!z) + return -ENODEV; + + if (add_uevent_var(env, "ZORRO_ID=%08X", z->id) || + add_uevent_var(env, "ZORRO_SLOT_NAME=%s", dev_name(dev)) || + add_uevent_var(env, "ZORRO_SLOT_ADDR=%04X", z->slotaddr) || + add_uevent_var(env, "MODALIAS=" ZORRO_DEVICE_MODALIAS_FMT, z->id)) + return -ENOMEM; + + return 0; +#else /* !CONFIG_HOTPLUG */ + return -ENODEV; +#endif /* !CONFIG_HOTPLUG */ +} struct bus_type zorro_bus_type = { .name = "zorro", .match = zorro_bus_match, + .uevent = zorro_uevent, .probe = zorro_device_probe, .remove = zorro_device_remove, }; diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index 1d2a772ea14c..eb924e0a64ce 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -77,6 +77,16 @@ static struct bin_attribute zorro_config_attr = { .read = zorro_read_config, }; +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct zorro_dev *z = to_zorro_dev(dev); + + return sprintf(buf, ZORRO_DEVICE_MODALIAS_FMT "\n", z->id); +} + +static DEVICE_ATTR(modalias, S_IRUGO, modalias_show, NULL); + int zorro_create_sysfs_dev_files(struct zorro_dev *z) { struct device *dev = &z->dev; @@ -89,6 +99,7 @@ int zorro_create_sysfs_dev_files(struct zorro_dev *z) (error = device_create_file(dev, &dev_attr_slotaddr)) || (error = device_create_file(dev, &dev_attr_slotsize)) || (error = device_create_file(dev, &dev_attr_resource)) || + (error = device_create_file(dev, &dev_attr_modalias)) || (error = sysfs_create_bin_file(&dev->kobj, &zorro_config_attr))) return error; diff --git a/drivers/zorro/zorro.c b/drivers/zorro/zorro.c index d45fb34e2d23..6455f3a244c5 100644 --- a/drivers/zorro/zorro.c +++ b/drivers/zorro/zorro.c @@ -15,6 +15,8 @@ #include <linux/zorro.h> #include <linux/bitops.h> #include <linux/string.h> +#include <linux/platform_device.h> +#include <linux/slab.h> #include <asm/setup.h> #include <asm/amigahw.h> @@ -26,24 +28,17 @@ * Zorro Expansion Devices */ -u_int zorro_num_autocon = 0; +unsigned int zorro_num_autocon; struct zorro_dev zorro_autocon[ZORRO_NUM_AUTO]; /* - * Single Zorro bus + * Zorro bus */ -struct zorro_bus zorro_bus = {\ - .resources = { - /* Zorro II regions (on Zorro II/III) */ - { .name = "Zorro II exp", .start = 0x00e80000, .end = 0x00efffff }, - { .name = "Zorro II mem", .start = 0x00200000, .end = 0x009fffff }, - /* Zorro III regions (on Zorro III only) */ - { .name = "Zorro III exp", .start = 0xff000000, .end = 0xffffffff }, - { .name = "Zorro III cfg", .start = 0x40000000, .end = 0x7fffffff } - }, - .name = "Zorro bus" +struct zorro_bus { + struct list_head devices; /* list of devices on this bus */ + struct device dev; }; @@ -53,18 +48,19 @@ struct zorro_bus zorro_bus = {\ struct zorro_dev *zorro_find_device(zorro_id id, struct zorro_dev *from) { - struct zorro_dev *z; + struct zorro_dev *z; - if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(ZORRO)) - return NULL; + if (!zorro_num_autocon) + return NULL; - for (z = from ? from+1 : &zorro_autocon[0]; - z < zorro_autocon+zorro_num_autocon; - z++) - if (id == ZORRO_WILDCARD || id == z->id) - return z; - return NULL; + for (z = from ? from+1 : &zorro_autocon[0]; + z < zorro_autocon+zorro_num_autocon; + z++) + if (id == ZORRO_WILDCARD || id == z->id) + return z; + return NULL; } +EXPORT_SYMBOL(zorro_find_device); /* @@ -83,121 +79,138 @@ struct zorro_dev *zorro_find_device(zorro_id id, struct zorro_dev *from) */ DECLARE_BITMAP(zorro_unused_z2ram, 128); +EXPORT_SYMBOL(zorro_unused_z2ram); static void __init mark_region(unsigned long start, unsigned long end, int flag) { - if (flag) - start += Z2RAM_CHUNKMASK; - else - end += Z2RAM_CHUNKMASK; - start &= ~Z2RAM_CHUNKMASK; - end &= ~Z2RAM_CHUNKMASK; - - if (end <= Z2RAM_START || start >= Z2RAM_END) - return; - start = start < Z2RAM_START ? 0x00000000 : start-Z2RAM_START; - end = end > Z2RAM_END ? Z2RAM_SIZE : end-Z2RAM_START; - while (start < end) { - u32 chunk = start>>Z2RAM_CHUNKSHIFT; if (flag) - set_bit(chunk, zorro_unused_z2ram); + start += Z2RAM_CHUNKMASK; else - clear_bit(chunk, zorro_unused_z2ram); - start += Z2RAM_CHUNKSIZE; - } + end += Z2RAM_CHUNKMASK; + start &= ~Z2RAM_CHUNKMASK; + end &= ~Z2RAM_CHUNKMASK; + + if (end <= Z2RAM_START || start >= Z2RAM_END) + return; + start = start < Z2RAM_START ? 0x00000000 : start-Z2RAM_START; + end = end > Z2RAM_END ? Z2RAM_SIZE : end-Z2RAM_START; + while (start < end) { + u32 chunk = start>>Z2RAM_CHUNKSHIFT; + if (flag) + set_bit(chunk, zorro_unused_z2ram); + else + clear_bit(chunk, zorro_unused_z2ram); + start += Z2RAM_CHUNKSIZE; + } } -static struct resource __init *zorro_find_parent_resource(struct zorro_dev *z) +static struct resource __init *zorro_find_parent_resource( + struct platform_device *bridge, struct zorro_dev *z) { - int i; + int i; - for (i = 0; i < zorro_bus.num_resources; i++) - if (zorro_resource_start(z) >= zorro_bus.resources[i].start && - zorro_resource_end(z) <= zorro_bus.resources[i].end) - return &zorro_bus.resources[i]; - return &iomem_resource; + for (i = 0; i < bridge->num_resources; i++) { + struct resource *r = &bridge->resource[i]; + if (zorro_resource_start(z) >= r->start && + zorro_resource_end(z) <= r->end) + return r; + } + return &iomem_resource; } - /* - * Initialization - */ -static int __init zorro_init(void) +static int __init amiga_zorro_probe(struct platform_device *pdev) { - struct zorro_dev *z; - unsigned int i; - int error; - - if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(ZORRO)) - return 0; - - pr_info("Zorro: Probing AutoConfig expansion devices: %d device%s\n", - zorro_num_autocon, zorro_num_autocon == 1 ? "" : "s"); - - /* Initialize the Zorro bus */ - INIT_LIST_HEAD(&zorro_bus.devices); - dev_set_name(&zorro_bus.dev, "zorro"); - error = device_register(&zorro_bus.dev); - if (error) { - pr_err("Zorro: Error registering zorro_bus\n"); - return error; - } - - /* Request the resources */ - zorro_bus.num_resources = AMIGAHW_PRESENT(ZORRO3) ? 4 : 2; - for (i = 0; i < zorro_bus.num_resources; i++) - request_resource(&iomem_resource, &zorro_bus.resources[i]); - - /* Register all devices */ - for (i = 0; i < zorro_num_autocon; i++) { - z = &zorro_autocon[i]; - z->id = (z->rom.er_Manufacturer<<16) | (z->rom.er_Product<<8); - if (z->id == ZORRO_PROD_GVP_EPC_BASE) { - /* GVP quirk */ - unsigned long magic = zorro_resource_start(z)+0x8000; - z->id |= *(u16 *)ZTWO_VADDR(magic) & GVP_PRODMASK; - } - sprintf(z->name, "Zorro device %08x", z->id); - zorro_name_device(z); - z->resource.name = z->name; - if (request_resource(zorro_find_parent_resource(z), &z->resource)) - pr_err("Zorro: Address space collision on device %s %pR\n", - z->name, &z->resource); - dev_set_name(&z->dev, "%02x", i); - z->dev.parent = &zorro_bus.dev; - z->dev.bus = &zorro_bus_type; - error = device_register(&z->dev); + struct zorro_bus *bus; + struct zorro_dev *z; + struct resource *r; + unsigned int i; + int error; + + /* Initialize the Zorro bus */ + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + + INIT_LIST_HEAD(&bus->devices); + bus->dev.parent = &pdev->dev; + dev_set_name(&bus->dev, "zorro"); + error = device_register(&bus->dev); if (error) { - pr_err("Zorro: Error registering device %s\n", z->name); - continue; + pr_err("Zorro: Error registering zorro_bus\n"); + kfree(bus); + return error; } - error = zorro_create_sysfs_dev_files(z); - if (error) - dev_err(&z->dev, "Error creating sysfs files\n"); - } - - /* Mark all available Zorro II memory */ - zorro_for_each_dev(z) { - if (z->rom.er_Type & ERTF_MEMLIST) - mark_region(zorro_resource_start(z), zorro_resource_end(z)+1, 1); - } - - /* Unmark all used Zorro II memory */ - for (i = 0; i < m68k_num_memory; i++) - if (m68k_memory[i].addr < 16*1024*1024) - mark_region(m68k_memory[i].addr, - m68k_memory[i].addr+m68k_memory[i].size, 0); - - return 0; + platform_set_drvdata(pdev, bus); + + /* Register all devices */ + pr_info("Zorro: Probing AutoConfig expansion devices: %u device%s\n", + zorro_num_autocon, zorro_num_autocon == 1 ? "" : "s"); + + for (i = 0; i < zorro_num_autocon; i++) { + z = &zorro_autocon[i]; + z->id = (z->rom.er_Manufacturer<<16) | (z->rom.er_Product<<8); + if (z->id == ZORRO_PROD_GVP_EPC_BASE) { + /* GVP quirk */ + unsigned long magic = zorro_resource_start(z)+0x8000; + z->id |= *(u16 *)ZTWO_VADDR(magic) & GVP_PRODMASK; + } + sprintf(z->name, "Zorro device %08x", z->id); + zorro_name_device(z); + z->resource.name = z->name; + r = zorro_find_parent_resource(pdev, z); + error = request_resource(r, &z->resource); + if (error) + dev_err(&bus->dev, + "Address space collision on device %s %pR\n", + z->name, &z->resource); + dev_set_name(&z->dev, "%02x", i); + z->dev.parent = &bus->dev; + z->dev.bus = &zorro_bus_type; + error = device_register(&z->dev); + if (error) { + dev_err(&bus->dev, "Error registering device %s\n", + z->name); + continue; + } + error = zorro_create_sysfs_dev_files(z); + if (error) + dev_err(&z->dev, "Error creating sysfs files\n"); + } + + /* Mark all available Zorro II memory */ + zorro_for_each_dev(z) { + if (z->rom.er_Type & ERTF_MEMLIST) + mark_region(zorro_resource_start(z), + zorro_resource_end(z)+1, 1); + } + + /* Unmark all used Zorro II memory */ + for (i = 0; i < m68k_num_memory; i++) + if (m68k_memory[i].addr < 16*1024*1024) + mark_region(m68k_memory[i].addr, + m68k_memory[i].addr+m68k_memory[i].size, + 0); + + return 0; } -subsys_initcall(zorro_init); +static struct platform_driver amiga_zorro_driver = { + .driver = { + .name = "amiga-zorro", + .owner = THIS_MODULE, + }, +}; -EXPORT_SYMBOL(zorro_find_device); -EXPORT_SYMBOL(zorro_unused_z2ram); +static int __init amiga_zorro_init(void) +{ + return platform_driver_probe(&amiga_zorro_driver, amiga_zorro_probe); +} + +module_init(amiga_zorro_init); MODULE_LICENSE("GPL"); |