sipl: W/A to produce precise 30hz signal

With current implementation we only program 2 edge registers.
Because of which for 30Hz freq we are able to produce wave with 29.99hz
freq which is causing drfit which aligning with external time source.

For the workaround we are addding extra periods using edge registers to
precisely produce the 30hz wave form only if the fsync-group contains
generators with 30hz freq.

Bug 4584554

Change-Id: I71e98ca29c8189adbe28511d09cf3e0b75551d22
Signed-off-by: Mohit Ingale <mohiti@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3129503
(cherry picked from commit ac4236cc3e338b5a705e2310efba9593bf672002)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3130953
Reviewed-by: Shiva Dubey <sdubey@nvidia.com>
Reviewed-by: Frank Chen <frankc@nvidia.com>
Reviewed-by: Jagadeesh Kinni <jkinni@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Mohit Ingale
2024-04-01 09:33:25 +00:00
committed by mobile promotions
parent 120be4313a
commit 035dd3db37

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
*/
#include <nvidia/conftest.h>
@@ -24,6 +24,7 @@
#include <linux/pm.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/gcd.h>
#include <uapi/media/cam_fsync.h>
@@ -314,10 +315,65 @@ static int cam_fsync_add_generator(struct fsync_generator_group *group, struct d
return err;
}
struct cam_fsync_extra_ticks_and_period {
u32 extra_ticks;
u32 num_periods;
};
/**
* @brief Get the Extra Ticks And Period For 30 Hz object
*
* This function does the following:
* - Calculate the extra ticks and number of periods for a 30Hz signal using the
* fractional part of the division between TSC_TICKS_PER_HZ and the frequency.
* - The smallest fraction is calculated by dividing with the @ref GCD of the
* frequency and the remainder.
*
* @param[out] extra Extra ticks and number of periods
*/
static void cam_fsync_get_extra_ticks_and_period_for_30hz(
struct cam_fsync_extra_ticks_and_period *extra)
{
u32 const frequency = 30U;
u32 const remainder = TSC_TICKS_PER_HZ % frequency;
u32 const hcf = gcd(frequency, remainder);
extra->extra_ticks = remainder / hcf;
extra->num_periods = frequency / hcf;
}
/**
* @brief Function to check if precise signal can be generated
*
* This function does the following:
* - Check if all generators in the group are 30hz.
*
* @param group Pointer to the fsync generator group
*
* @return bool True if all generators are 30hz, false otherwise
*/
static bool cam_fsync_can_generate_precise_freq(
struct fsync_generator_group *group)
{
struct cam_fsync_generator *generator;
bool is_30hz = true;
list_for_each_entry(generator, &group->generators, list) {
if (generator->config.freq_hz != 30) {
is_30hz = false;
break;
}
}
return is_30hz;
}
static int cam_fsync_program_group_generator_edges(struct fsync_generator_group *group)
{
struct cam_fsync_generator *generator;
u32 max_freq_hz_lcm = 0;
struct cam_fsync_extra_ticks_and_period extra = {0, 1};
bool const can_generate_precise_freq = cam_fsync_can_generate_precise_freq(group);
/*
* If rational locking is enforced (e.g. a 30Hz & 60Hz signal must align every two periods
@@ -338,10 +394,23 @@ static int cam_fsync_program_group_generator_edges(struct fsync_generator_group
}
}
/**
* Generating a freq with period that is not multiple of TSC unit will
* cause the signal to drift over time. To avoid this, if precise signal
* is supported, calculate the extra ticks over the number of periods
* using @ref getextra_ticksAndPeriodFor30Hz() for accurate phase alignment.
* If the signal is not precise, set the extra ticks to 0 and number of
* periods to 1 which is the default case.
*/
if (can_generate_precise_freq)
cam_fsync_get_extra_ticks_and_period_for_30hz(&extra);
list_for_each_entry(generator, &group->generators, list) {
u32 ticks_in_period = 0;
u32 ticks_active = 0;
u32 ticks_inactive = 0;
u32 const edge_reg_offset = 8U;
u32 i = 0;
if (group->features->rational_locking.enforced) {
ticks_in_period = DIV_ROUND_CLOSEST(TSC_TICKS_PER_HZ, max_freq_hz_lcm);
@@ -350,18 +419,43 @@ static int cam_fsync_program_group_generator_edges(struct fsync_generator_group
ticks_in_period = DIV_ROUND_CLOSEST(TSC_TICKS_PER_HZ,
generator->config.freq_hz);
}
ticks_active = mult_frac(ticks_in_period, generator->config.duty_cycle, 100);
ticks_inactive = ticks_in_period - ticks_active;
cam_fsync_generator_writel(generator, TSC_GENX_EDGE0,
TSC_GENX_EDGEX_TOGGLE |
FIELD_PREP(TSC_GENX_EDGEX_OFFSET, ticks_active));
/**
* Program the edge registers for the generator. There are 8 edge registers
* available for each generator. For each period, program the active and
* inactive ticks. If extra ticks are available, apply them over the
* inactive ticks across the number of periods.
* For each period, 2 edge registers are programmed, one for active and one
* for inactive ticks based on the duty cycle.
*/
for (i = 0; i < extra.num_periods; i++) {
u32 flags = TSC_GENX_EDGEX_TOGGLE;
u32 extra_ticks = 0;
cam_fsync_generator_writel(generator, TSC_GENX_EDGE1,
TSC_GENX_EDGEX_TOGGLE |
TSC_GENX_EDGEX_LOOP |
FIELD_PREP(TSC_GENX_EDGEX_OFFSET, ticks_inactive));
cam_fsync_generator_writel(generator,
TSC_GENX_EDGE0 + (i * edge_reg_offset),
flags |
FIELD_PREP(TSC_GENX_EDGEX_OFFSET, ticks_active));
/**
* If it is the last period, set the loop flag for the last edge register
* to loop back to the first edge register.
*/
if (i == extra.num_periods - 1)
flags |= TSC_GENX_EDGEX_LOOP;
if (extra.extra_ticks) {
extra_ticks = 1;
extra.extra_ticks--;
}
cam_fsync_generator_writel(generator,
TSC_GENX_EDGE1 + (i * edge_reg_offset),
flags |
FIELD_PREP(TSC_GENX_EDGEX_OFFSET, ticks_inactive + extra_ticks));
}
}
return 0;