media: ccs: Add the generator for CCS register definitions and limits

Add register definitions of the MIPI CCS 1.1 standard.

The CCS driver makes extended use of device's capability registers that
are dependent on CCS version. This involves having an in-memory data
structure for limit and capability information, creating that data
structure and accessing it.

The register definitions as well as the definitions of this data structure
are generated from a text file using a Perl script. Add the generator
script to make it easy to update the generated files.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Sakari Ailus 2020-02-03 10:39:24 +01:00 committed by Mauro Carvalho Chehab
parent 67e061f044
commit 1ec0b899c2
3 changed files with 1475 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,433 @@
#!/usr/bin/perl -w
# SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
# Copyright (C) 2019--2020 Intel Corporation
use Getopt::Long qw(:config no_ignore_case);
use File::Basename;
my $ccsregs = "ccs-regs.txt";
my $header;
my $regarray;
my $limitc;
my $limith;
my $kernel;
my $help;
GetOptions("ccsregs|c=s" => \$ccsregs,
"header|e=s" => \$header,
"regarray|r=s" => \$regarray,
"limitc|l=s" => \$limitc,
"limith|L=s" => \$limith,
"kernel|k" => \$kernel,
"help|h" => \$help) or die "can't parse options";
$help = 1 if ! defined $header || ! defined $limitc || ! defined $limith;
if (defined $help) {
print <<EOH
$0 - Create CCS register definitions for C
usage: $0 -c ccs-regs.txt -e header -r regarray -l limit-c -L limit-header [-k]
-c ccs register file
-e header file name
-r register description array file name
-l limit and capability array file name
-L limit and capability header file name
-k generate files for kernel space consumption
EOH
;
exit 0;
}
my $lh_hdr = ! defined $kernel
? '#include "ccs-os.h"' . "\n"
: "#include <linux/bits.h>\n#include <linux/types.h>\n";
my $uint32_t = ! defined $kernel ? 'uint32_t' : 'u32';
my $uint16_t = ! defined $kernel ? 'uint16_t' : 'u16';
open(my $R, "< $ccsregs") or die "can't open $ccsregs";
open(my $H, "> $header") or die "can't open $header";
my $A;
if (defined $regarray) {
open($A, "> $regarray") or die "can't open $regarray";
}
open(my $LC, "> $limitc") or die "can't open $limitc";
open(my $LH, "> $limith") or die "can't open $limith";
my %this;
sub is_limit_reg($) {
my $addr = hex $_[0];
return 0 if $addr < 0x40; # weed out status registers
return 0 if $addr >= 0x100 && $addr < 0xfff; # weed out configuration registers
return 1;
}
my $uc_header = basename uc $header;
$uc_header =~ s/[^A-Z0-9]/_/g;
my $copyright = "/* Copyright (C) 2019--2020 Intel Corporation */\n";
my $license = "SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause";
for my $fh ($A, $LC) {
print $fh "// $license\n$copyright\n" if defined $fh;
}
for my $fh ($H, $LH) {
print $fh "/* $license */\n$copyright\n";
}
sub bit_def($) {
my $bit = shift @_;
return "BIT($bit)" if defined $kernel;
return "(1U << $bit)" if $bit =~ /^[a-zA-Z0-9_]+$/;
return "(1U << ($bit))";
}
print $H <<EOF
#ifndef __${uc_header}__
#define __${uc_header}__
EOF
;
print $H "#include <linux/bits.h>\n\n" if defined $kernel;
print $H <<EOF
#define CCS_FL_BASE 16
EOF
;
print $H "#define CCS_FL_16BIT " . bit_def("CCS_FL_BASE") . "\n";
print $H "#define CCS_FL_32BIT " . bit_def("CCS_FL_BASE + 1") . "\n";
print $H "#define CCS_FL_FLOAT_IREAL " . bit_def("CCS_FL_BASE + 2") . "\n";
print $H "#define CCS_FL_IREAL " . bit_def("CCS_FL_BASE + 3") . "\n";
print $H <<EOF
#define CCS_R_ADDR(r) ((r) & 0xffff)
EOF
;
print $A <<EOF
#include <stdint.h>
#include <stdio.h>
#include "ccs-extra.h"
#include "ccs-regs.h"
EOF
if defined $A;
my $uc_limith = basename uc $limith;
$uc_limith =~ s/[^A-Z0-9]/_/g;
print $LH <<EOF
#ifndef __${uc_limith}__
#define __${uc_limith}__
$lh_hdr
struct ccs_limit {
$uint32_t reg;
$uint16_t size;
$uint16_t flags;
const char *name;
};
EOF
;
print $LH "#define CCS_L_FL_SAME_REG " . bit_def(0) . "\n\n";
print $LH <<EOF
extern const struct ccs_limit ccs_limits[];
EOF
;
print $LC <<EOF
#include "ccs-limits.h"
#include "ccs-regs.h"
const struct ccs_limit ccs_limits[] = {
EOF
;
my $limitcount = 0;
my $argdescs;
my $reglist = "const struct ccs_reg_desc ccs_reg_desc[] = {\n";
sub name_split($$) {
my ($name, $addr) = @_;
my $args;
$name =~ /([^\(]+?)(\(.*)/;
($name, $args) = ($1, $2);
$args = [split /,\s*/, $args];
foreach my $t (@$args) {
$t =~ s/[\(\)]//g;
$t =~ s/\//\\\//g;
}
return ($name, $addr, $args);
}
sub tabconv($) {
$_ = shift;
my @l = split "\n", $_;
map {
s/ {8,8}/\t/g;
s/\t\K +//;
} @l;
return (join "\n", @l) . "\n";
}
sub elem_size(@) {
my @flags = @_;
return 2 if grep /^16$/, @flags;
return 4 if grep /^32$/, @flags;
return 1;
}
sub arr_size($) {
my $this = $_[0];
my $size = $this->{elsize};
my $h = $this->{argparams};
foreach my $arg (@{$this->{args}}) {
my $apref = $h->{$arg};
$size *= $apref->{max} - $apref->{min} + 1;
}
return $size;
}
sub print_args($$$) {
my ($this, $postfix, $is_same_reg) = @_;
my ($args, $argparams, $name) =
($this->{args}, $this->{argparams}, $this->{name});
my $varname = "ccs_reg_arg_" . (lc $name) . $postfix;
my @mins;
my @sorted_args = @{$this->{sorted_args}};
my $lim_arg;
my $size = arr_size($this);
$argdescs .= "static const struct ccs_reg_arg " . $varname . "[] = {\n";
foreach my $sorted_arg (@sorted_args) {
push @mins, $argparams->{$sorted_arg}->{min};
}
foreach my $sorted_arg (@sorted_args) {
my $h = $argparams->{$sorted_arg};
$argdescs .= "\t{ \"$sorted_arg\", $h->{min}, $h->{max}, $h->{elsize} },\n";
$lim_arg .= defined $lim_arg ? ", $h->{min}" : "$h->{min}";
}
$argdescs .= "};\n\n";
$reglist .= "\t{ CCS_R_" . (uc $name) . "(" . (join ",", (@mins)) .
"), $size, sizeof($varname) / sizeof(*$varname)," .
" \"" . (lc $name) . "\", $varname },\n";
print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . "($lim_arg), " .
$size . ", " . ($is_same_reg ? "CCS_L_FL_SAME_REG" : "0") .
", \"$name" . (defined $this->{discontig} ? " $lim_arg" : "") . "\" },\n"
if is_limit_reg $this->{base_addr};
}
my $hdr_data;
while (<$R>) {
chop;
s/^\s*//;
next if /^[#;]/ || /^$/;
if (s/^-\s*//) {
if (s/^b\s*//) {
my ($bit, $addr) = split /\t+/;
$bit = uc $bit;
$hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) ."_$bit", bit_def($addr) . "\n";
} elsif (s/^f\s*//) {
s/[,\.-]/_/g;
my @a = split /\s+/;
my ($msb, $lsb, $this_field) = reverse @a;
@a = ( { "name" => "SHIFT", "addr" => $lsb, "fmt" => "%uU", },
{ "name" => "MASK", "addr" => (1 << ($msb + 1)) - 1 - ((1 << $lsb) - 1), "fmt" => "0x%" . join(".", ($this{"elsize"} >> 2) x 2) . "x" } );
$this{"field"} = $this_field;
foreach my $ar (@a) {
#print $ar->{fmt}."\n";
$hdr_data .= sprintf "#define %-62s " . $ar->{"fmt"} . "\n", "CCS_" . (uc $this{"name"}) . (defined $this_field ? "_" . uc $this_field : "") . "_" . $ar->{"name"}, $ar->{"addr"} . "\n";
}
} elsif (s/^e\s*//) {
s/[,\.-]/_/g;
my ($enum, $addr) = split /\s+/;
$enum = uc $enum;
$hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) . (defined $this{"field"} ? "_" . uc $this{"field"} : "") ."_$enum", $addr . ($addr =~ /0x/i ? "" : "U") . "\n";
} elsif (s/^l\s*//) {
my ($arg, $min, $max, $elsize, @discontig) = split /\s+/;
my $size;
foreach my $num ($min, $max) {
$num = hex $num if $num =~ /0x/i;
}
$hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MIN_$arg"), $min . ($min =~ /0x/i ? "" : "U") . "\n";
$hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MAX_$arg"), $max . ($max =~ /0x/i ? "" : "U") . "\n";
my $h = $this{argparams};
$h->{$arg} = { "min" => $min,
"max" => $max,
"elsize" => $elsize =~ /^0x/ ? hex $elsize : $elsize,
"discontig" => \@discontig };
$this{discontig} = $arg if @discontig;
next if $#{$this{args}} + 1 != scalar keys %{$this{argparams}};
my $reg_formula = "($this{addr}";
my $lim_formula;
foreach my $arg (@{$this{args}}) {
my $d = $h->{$arg}->{discontig};
my $times = $h->{$arg}->{elsize} != 1 ?
" * " . $h->{$arg}->{elsize} : "";
if (@$d) {
my ($lim, $offset) = split /,/, $d->[0];
$reg_formula .= " + (($arg) < $lim ? ($arg)$times : $offset + (($arg) - $lim)$times)";
} else {
$reg_formula .= " + ($arg)$times";
}
$lim_formula .= (defined $lim_formula ? " + " : "") . "($arg)$times";
}
$reg_formula .= ")\n";
$lim_formula =~ s/^\(([a-z0-9]+)\)$/$1/i;
print $H tabconv sprintf("#define %-62s %s", "CCS_R_" . (uc $this{name}) .
$this{arglist}, $reg_formula);
print $H tabconv $hdr_data;
undef $hdr_data;
# Sort arguments in descending order by size
@{$this{sorted_args}} = sort {
$h->{$a}->{elsize} <= $h->{$b}->{elsize}
} @{$this{args}};
if (defined $this{discontig}) {
my $da = $this{argparams}->{$this{discontig}};
my ($first_discontig) = split /,/, $da->{discontig}->[0];
my $max = $da->{max};
$da->{max} = $first_discontig - 1;
print_args(\%this, "", 0);
$da->{min} = $da->{max} + 1;
$da->{max} = $max;
print_args(\%this, $first_discontig, 1);
} else {
print_args(\%this, "", 0);
}
next unless is_limit_reg $this{base_addr};
print $LH tabconv sprintf "#define %-63s%s\n",
"CCS_L_" . (uc $this{name}) . "_OFFSET(" .
(join ", ", @{$this{args}}) . ")", "($lim_formula)";
}
if (! @{$this{args}}) {
print $H tabconv($hdr_data);
undef $hdr_data;
}
next;
}
my ($name, $addr, @flags) = split /\t+/, $_;
my $args = [];
my $sp;
($name, $addr, $args) = name_split($name, $addr) if /\(.*\)/;
$name =~ s/[,\.-]/_/g;
my $flagstring = "";
my $size = elem_size(@flags);
$flagstring .= "| CCS_FL_16BIT " if $size eq "2";
$flagstring .= "| CCS_FL_32BIT " if $size eq "4";
$flagstring .= "| CCS_FL_FLOAT_IREAL " if grep /^float_ireal$/, @flags;
$flagstring .= "| CCS_FL_IREAL " if grep /^ireal$/, @flags;
$flagstring =~ s/^\| //;
$flagstring =~ s/ $//;
$flagstring = "($flagstring)" if $flagstring =~ /\|/;
my $base_addr = $addr;
$addr = "($addr | $flagstring)" if $flagstring ne "";
my $arglist = @$args ? "(" . (join ", ", @$args) . ")" : "";
$hdr_data .= sprintf "#define %-62s %s\n", "CCS_R_" . (uc $name), $addr
if !@$args;
$name =~ s/\(.*//;
%this = ( name => $name,
addr => $addr,
base_addr => $base_addr,
argparams => {},
args => $args,
arglist => $arglist,
elsize => $size,
);
if (!@$args) {
$reglist .= "\t{ CCS_R_" . (uc $name) . ", 1, 0, \"" . (lc $name) . "\", NULL },\n";
print $H tabconv $hdr_data;
undef $hdr_data;
print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . ", " .
$this{elsize} . ", 0, \"$name\" },\n"
if is_limit_reg $this{base_addr};
}
print $LH tabconv sprintf "#define %-63s%s\n",
"CCS_L_" . (uc $this{name}), $limitcount++
if is_limit_reg $this{base_addr};
}
if (defined $A) {
print $A $argdescs, $reglist;
print $A "\t{ 0 }\n";
print $A "};\n";
}
print $H "\n#endif /* __${uc_header}__ */\n";
print $LH tabconv sprintf "#define %-63s%s\n", "CCS_L_LAST", $limitcount;
print $LH "\n#endif /* __${uc_limith}__ */\n";
print $LC "\t{ 0 } /* Guardian */\n";
print $LC "};\n";
close($R);
close($H);
close($A) if defined $A;
close($LC);
close($LH);

View file

@ -16108,6 +16108,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/i2c/nokia,smia.txt
F: Documentation/driver-api/media/drivers/ccs/
F: drivers/media/i2c/smiapp-pll.c
F: drivers/media/i2c/smiapp-pll.h
F: drivers/media/i2c/smiapp/