@@ -329,6 +329,12 @@ static const struct hwm_cmd ewb = {
329329 .magic_word = 0xCDAB ,
330330 .opcode = 0x18 ,
331331};
332+
333+ static const struct hwm_cmd cmd_hw_reset = {
334+ .header = 0x14 ,
335+ .magic_word = 0xCDAB ,
336+ .opcode = 0x20 , /* HW reset opcode */
337+ };
332338#ifdef CONFIG_VIDEO_INTEL_IPU6
333339static const s64 link_freq_menu_items [] = {
334340 D4XX_LINK_FREQ_360MHZ ,
@@ -2141,6 +2147,9 @@ static int ds5_hw_set_exposure(struct ds5 *state, u32 base, s32 val)
21412147 */
21422148#define DS5_CAMERA_CID_HWMC_RW (DS5_CAMERA_CID_BASE+32)
21432149
2150+ /* HW reset with recovery for GMSL connections */
2151+ #define DS5_CAMERA_CID_HW_RESET (DS5_CAMERA_CID_BASE+33)
2152+
21442153#define DS5_HWMC_DATA 0x4900
21452154#define DS5_HWMC_STATUS 0x4904
21462155#define DS5_HWMC_RESP_LEN 0x4908
@@ -2277,6 +2286,146 @@ static int ds5_set_calibration_data(struct ds5 *state,
22772286 return ret ;
22782287}
22792288
2289+ /* HW reset timeout and polling parameters */
2290+ #define DS5_HW_RESET_INITIAL_DELAY_MS 100
2291+ #define DS5_HW_RESET_POLL_INTERVAL_MS 200
2292+ #define DS5_HW_RESET_TIMEOUT_MS 10000
2293+ #define DS5_HW_RESET_MAX_RETRIES (DS5_HW_RESET_TIMEOUT_MS / DS5_HW_RESET_POLL_INTERVAL_MS)
2294+
2295+ /*
2296+ * Register 0x5020 status values (from firmware):
2297+ * - 0xDEAD: Device in normal UVC mode (ready) - ICT returns error for unhandled cmd
2298+ * - 0x04030201: Device in DFU mode (DFU magic bytes, little-endian)
2299+ * - I2C error: Device not yet responding (still resetting)
2300+ */
2301+ #define DS5_HW_RESET_STATUS_READY 0xDEAD
2302+ #define DS5_HW_RESET_DFU_MAGIC_LSW 0x0201 /* Lower 16 bits of 0x04030201 */
2303+
2304+ /*
2305+ * ds5_hw_reset_with_recovery - Perform hardware reset with GMSL recovery
2306+ * @state: Driver state structure
2307+ *
2308+ * This function sends a hardware reset command to the D4XX device and
2309+ * waits for it to come back online. For GMSL connections, unlike USB,
2310+ * there is no automatic re-enumeration, so we must poll the device
2311+ * until it becomes responsive again.
2312+ *
2313+ * Returns 0 on success, negative error code on failure.
2314+ */
2315+ static int ds5_hw_reset_with_recovery (struct ds5 * state )
2316+ {
2317+ int ret ;
2318+ int retry ;
2319+ u16 status = 0 ;
2320+ bool device_went_down = false;
2321+
2322+ dev_info (& state -> client -> dev , "%s(): Initiating HW reset with recovery\n" ,
2323+ __func__ );
2324+
2325+ /* 1. Send HW reset command */
2326+ ret = ds5_raw_write (state , DS5_HWMC_DATA , & cmd_hw_reset , sizeof (cmd_hw_reset ));
2327+ if (ret < 0 ) {
2328+ dev_err (& state -> client -> dev , "%s(): Failed to write HW reset command: %d\n" ,
2329+ __func__ , ret );
2330+ return ret ;
2331+ }
2332+
2333+ ret = ds5_write (state , DS5_HWMC_EXEC , 0x01 );
2334+ if (ret < 0 ) {
2335+ dev_err (& state -> client -> dev , "%s(): Failed to execute HW reset command: %d\n" ,
2336+ __func__ , ret );
2337+ return ret ;
2338+ }
2339+
2340+ dev_info (& state -> client -> dev , "%s(): HW reset command sent, waiting for device...\n" ,
2341+ __func__ );
2342+
2343+ /* 2. Brief delay to allow reset to begin */
2344+ msleep (DS5_HW_RESET_INITIAL_DELAY_MS );
2345+
2346+ /* 3. Poll for device to come back online */
2347+ for (retry = 0 ; retry < DS5_HW_RESET_MAX_RETRIES ; retry ++ ) {
2348+ msleep (DS5_HW_RESET_POLL_INTERVAL_MS );
2349+
2350+ ret = ds5_read (state , 0x5020 , & status );
2351+
2352+ if (ret < 0 ) {
2353+ /* I2C failed - device is resetting (expected during reset) */
2354+ device_went_down = true;
2355+ dev_dbg (& state -> client -> dev ,
2356+ "%s(): Device not responding (resetting), retry %d\n" ,
2357+ __func__ , retry );
2358+ continue ;
2359+ }
2360+
2361+ /* I2C succeeded - check status */
2362+ if (status == DS5_HW_RESET_STATUS_READY ) {
2363+ /* 0xDEAD means device is in normal UVC mode (ready) */
2364+ dev_info (& state -> client -> dev ,
2365+ "%s(): Device ready after %d ms, status: 0x%04x\n" ,
2366+ __func__ ,
2367+ DS5_HW_RESET_INITIAL_DELAY_MS +
2368+ (retry + 1 ) * DS5_HW_RESET_POLL_INTERVAL_MS ,
2369+ status );
2370+ break ;
2371+ }
2372+
2373+ if (status == DS5_HW_RESET_DFU_MAGIC_LSW ) {
2374+ /* 0x0201 is lower 16-bits of DFU magic 0x04030201 */
2375+ dev_warn (& state -> client -> dev ,
2376+ "%s(): Device in DFU/recovery mode after reset\n" , __func__ );
2377+ state -> dfu_dev .dfu_state_flag = DS5_DFU_RECOVERY ;
2378+ return 0 ;
2379+ }
2380+
2381+ /* Other status - keep waiting */
2382+ dev_dbg (& state -> client -> dev ,
2383+ "%s(): Unexpected status 0x%04x, retry %d\n" ,
2384+ __func__ , status , retry );
2385+ }
2386+
2387+ if (retry >= DS5_HW_RESET_MAX_RETRIES ) {
2388+ dev_err (& state -> client -> dev ,
2389+ "%s(): Device did not become ready after %d ms (last status: 0x%04x, i2c ret: %d)\n" ,
2390+ __func__ , DS5_HW_RESET_INITIAL_DELAY_MS + DS5_HW_RESET_TIMEOUT_MS ,
2391+ status , ret );
2392+ return - ETIMEDOUT ;
2393+ }
2394+
2395+ #ifdef CONFIG_VIDEO_D4XX_SERDES
2396+ /* 4. Re-initialize SERDES link if available */
2397+ if (state -> dser_dev ) {
2398+ dev_info (& state -> client -> dev ,
2399+ "%s(): Re-initializing SERDES link\n" , __func__ );
2400+ max9296_reset_oneshot (state -> dser_dev );
2401+ msleep (300 );
2402+ }
2403+ #endif
2404+
2405+ /* 5. Verify device is operational by reading firmware version */
2406+ ret = ds5_read (state , DS5_FW_VERSION , & state -> fw_version );
2407+ if (ret < 0 ) {
2408+ dev_err (& state -> client -> dev ,
2409+ "%s(): Failed to read firmware version: %d\n" , __func__ , ret );
2410+ return ret ;
2411+ }
2412+
2413+ ret = ds5_read (state , DS5_FW_BUILD , & state -> fw_build );
2414+ if (ret < 0 ) {
2415+ dev_err (& state -> client -> dev ,
2416+ "%s(): Failed to read firmware build: %d\n" , __func__ , ret );
2417+ return ret ;
2418+ }
2419+
2420+ dev_info (& state -> client -> dev ,
2421+ "%s(): HW reset complete. Firmware: %d.%d.%d.%d\n" ,
2422+ __func__ ,
2423+ (state -> fw_version >> 8 ) & 0xff , state -> fw_version & 0xff ,
2424+ (state -> fw_build >> 8 ) & 0xff , state -> fw_build & 0xff );
2425+
2426+ return 0 ;
2427+ }
2428+
22802429static int ds5_mux_s_stream (struct v4l2_subdev * sd , int on );
22812430
22822431static int ds5_s_ctrl (struct v4l2_ctrl * ctrl )
@@ -2576,12 +2725,26 @@ static int ds5_s_ctrl(struct v4l2_ctrl *ctrl)
25762725 break ;
25772726 case DS5_CAMERA_CID_HWMC_RW :
25782727 if (ctrl -> p_new .p_u8 ) {
2728+ struct hwm_cmd * cmd = (struct hwm_cmd * )ctrl -> p_new .p_u8 ;
25792729 u16 size = * ((u8 * )ctrl -> p_new .p_u8 + 1 ) << 8 ;
25802730 size |= * ((u8 * )ctrl -> p_new .p_u8 + 0 );
2581- ret = ds5_send_hwmc (state , size + 4 ,
2582- (struct hwm_cmd * )ctrl -> p_new .p_u8 );
2731+
2732+ /* Check if this is a HW reset command (opcode 0x20) */
2733+ if (cmd -> opcode == 0x20 ) {
2734+ dev_info (& state -> client -> dev ,
2735+ "%s(): HW reset detected via HWMC_RW, using recovery path\n" ,
2736+ __func__ );
2737+ ret = ds5_hw_reset_with_recovery (state );
2738+ } else {
2739+ ret = ds5_send_hwmc (state , size + 4 , cmd );
2740+ }
25832741 }
25842742 break ;
2743+ case DS5_CAMERA_CID_HW_RESET :
2744+ dev_info (& state -> client -> dev , "%s(): HW reset requested via V4L2 control\n" ,
2745+ __func__ );
2746+ ret = ds5_hw_reset_with_recovery (state );
2747+ break ;
25852748 case DS5_CAMERA_CID_PWM :
25862749 if (state -> is_depth )
25872750 ret = ds5_write (state , base | DS5_PWM_FREQUENCY , ctrl -> val );
@@ -3161,6 +3324,18 @@ static const struct v4l2_ctrl_config ds5_ctrl_hwmc_rw = {
31613324 .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE ,
31623325};
31633326
3327+ static const struct v4l2_ctrl_config ds5_ctrl_hw_reset = {
3328+ .ops = & ds5_ctrl_ops ,
3329+ .id = DS5_CAMERA_CID_HW_RESET ,
3330+ .name = "HW Reset" ,
3331+ .type = V4L2_CTRL_TYPE_BUTTON ,
3332+ .min = 0 ,
3333+ .max = 1 ,
3334+ .step = 1 ,
3335+ .def = 0 ,
3336+ .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE ,
3337+ };
3338+
31643339static const struct v4l2_ctrl_config ds5_ctrl_pwm = {
31653340 .ops = & ds5_ctrl_ops ,
31663341 .id = DS5_CAMERA_CID_PWM ,
@@ -3908,6 +4083,7 @@ static int ds5_ctrl_init(struct ds5 *state, int sid)
39084083 ctrls -> ewb = v4l2_ctrl_new_custom (hdl , & ds5_ctrl_ewb , sensor );
39094084 ctrls -> hwmc = v4l2_ctrl_new_custom (hdl , & ds5_ctrl_hwmc , sensor );
39104085 v4l2_ctrl_new_custom (hdl , & ds5_ctrl_hwmc_rw , sensor );
4086+ v4l2_ctrl_new_custom (hdl , & ds5_ctrl_hw_reset , sensor );
39114087 }
39124088 // DEPTH custom
39134089 if (sid == DEPTH_SID )
@@ -6080,4 +6256,4 @@ MODULE_AUTHOR("Guennadi Liakhovetski <guennadi.liakhovetski@intel.com>,\n\
60806256 Shikun Ding <shikun.ding@intel.com>,\n\
60816257 Dmitry Perchanov <dmitry.perchanov@intel.com>" );
60826258MODULE_LICENSE ("GPL v2" );
6083- MODULE_VERSION ("1.0.2.4 " );
6259+ MODULE_VERSION ("1.0.2.5 " );
0 commit comments