Counts and average code
This is not very much different from your solution. It does not rely on the header being hardcoded though. Depends on GNU awk for the use of PROCINFO to control array traversal.

gawk -F, '
    {count[$2,$4]++; code[$2]; val[$4]}
    END {
        PROCINFO["sorted_in"] = "@ind_num_asc"
        printf "Code\t"
        dashes = "--------"
        for (v in val) {
            printf "%8d", v
            dashes = dashes "--------"
        }
        printf " =%8s\n", "Total"
        dashes = dashes "-----------"
        print dashes

        for (c in code) {
            sum_code = 0
            printf "%d\t", c
            for (v in val) {
                sum_code += count[c,v]
                sum_val[v] += count[c,v]
                printf "%8d", count[c,v]
            }
            printf " =%8d\n", sum_code
        }
        print dashes

        printf "Total\t"
        sum = 0
        for (v in val) {
            sum += sum_val[v]
            printf "%8d", sum_val[v]
        }
        printf " =%8d\n", sum
        print dashes

        printf "PCT\t"
        for (v in val) {
            printf "%8.1f", 100*sum_val[v]/sum
        }
        print "\n" dashes
    }
' file




Code          10      20      30      40 =   Total
---------------------------------------------------
2910           3       0       0       0 =       3
2911           0       2       0       0 =       2
2912           0       2       0       0 =       2
2914           2       2       1       0 =       5
2915           2       0       3       0 =       5
2916           0       0       0       5 =       5
2917           2       0       0       0 =       2
---------------------------------------------------
Total          9       6       4       5 =      24
---------------------------------------------------
PCT         37.5    25.0    16.7    20.8
---------------------------------------------------


I'm not a fan of ALL_CAPS_VARNAMES